Raymond Toy pushed to branch issue-459-more-accurate-dd-complex-div at cmucl / cmucl

Commits:

1 changed file:

Changes:

  • tests/float.lisp
    ... ... @@ -591,3 +591,211 @@
    591 591
       (:tag :issues)
    
    592 592
       (assert-equal -2w300
    
    593 593
     		(* -2w300 1w0)))
    
    594
    +
    
    595
    +
    
    596
    +
    
    597
    +;; Rudimentary code to read C %a formatted numbers that look like
    
    598
    +;; "-0x1.c4dba4ba1ee79p-620".  We assume STRING is exactly in this
    
    599
    +;; format.  No error-checking is done.
    
    600
    +(defun parse-hex-float (string)
    
    601
    +  (let* ((sign (if (char= (aref string 0) #\-)
    
    602
    +		   -1
    
    603
    +		   1))
    
    604
    +	 (dot-posn (position #\. string))
    
    605
    +	 (p-posn (position #\p string))
    
    606
    +	 (lead (parse-integer string :start (1- dot-posn) :end dot-posn))
    
    607
    +	 (frac (parse-integer string :start (1+ dot-posn) :end p-posn :radix 16))
    
    608
    +	 (exp (parse-integer string :start (1+ p-posn))))
    
    609
    +    (* sign
    
    610
    +       (scale-float (float (+ (ash lead 52)
    
    611
    +			      frac)
    
    612
    +			   1d0)
    
    613
    +		    (- exp 52)))))
    
    614
    +
    
    615
    +;; Relative error in terms of bits of accuracy.  This is the
    
    616
    +;; definition used by Baudin and Smith.  A result of 53 means the two
    
    617
    +;; numbers have identical bits.  For complex numbers, we use the min
    
    618
    +;; of the bits of accuracy of the real and imaginary parts.
    
    619
    +(defun rel-err (computed expected)
    
    620
    +  (flet ((rerr (c e)
    
    621
    +	   (let ((diff (abs (- c e))))
    
    622
    +	     (if (zerop diff)
    
    623
    +		 (float-digits diff)
    
    624
    +		 (floor (- (log (/ diff (abs e)) 2d0)))))))
    
    625
    +    (min (rerr (realpart computed) (realpart expected))
    
    626
    +	 (rerr (imagpart computed) (imagpart expected)))))
    
    627
    +
    
    628
    +(defun do-cdiv-test (x y z-true expected-rel)
    
    629
    +  (let* ((z (/ x y))
    
    630
    +	 (rel (rel-err z z-true)))
    
    631
    +    (assert-equal expected-rel
    
    632
    +		  rel
    
    633
    +		  x y z z-true rel)))
    
    634
    +;; Issue #456: improve accuracy of division of complex double-floats.
    
    635
    +;;
    
    636
    +;; Tests for complex division.  Tests 1-10 are from Baudin and Smith.
    
    637
    +;; Test 11 is an example from Maxima.  Test 12 is an example from the
    
    638
    +;; ansi-tests where (/ z z) didn't produce exactly 1.  Tests 13-16 are
    
    639
    +;; for examples for improvement iterations 1-4 from McGehearty.
    
    640
    +(macrolet
    
    641
    +    ((frob (name x y z-true rel)
    
    642
    +       `(define-test ,name
    
    643
    +	  (:tag :issues)
    
    644
    +	  (do-cdiv-test ,x ,y ,z-true ,rel))))
    
    645
    +  ;; First cases are from Baudin and Smith
    
    646
    +  ;; 1
    
    647
    +  (frob cdiv.baudin-case.1
    
    648
    +	(complex 1d0 1d0)
    
    649
    +	(complex 1d0 (scale-float 1d0 1023))
    
    650
    +	(complex (scale-float 1d0 -1023)
    
    651
    +		 (scale-float -1d0 -1023))
    
    652
    +	53)
    
    653
    +  ;; 2
    
    654
    +  (frob cdiv.baudin-case.2
    
    655
    +	(complex 1d0 1d0)
    
    656
    +	(complex (scale-float 1d0 -1023) (scale-float 1d0 -1023))
    
    657
    +	(complex (scale-float 1d0 1023) 0)
    
    658
    +	53)
    
    659
    +  ;; 3
    
    660
    +  (frob cdiv.baudin-case.3
    
    661
    +	(complex (scale-float 1d0 1023) (scale-float 1d0 -1023))
    
    662
    +	(complex (scale-float 1d0 677) (scale-float 1d0 -677))
    
    663
    +	(complex (scale-float 1d0 346) (scale-float -1d0 -1008))
    
    664
    +	53)
    
    665
    +  ;; 4
    
    666
    +  (frob cdiv.baudin-case.4.overflow
    
    667
    +	(complex (scale-float 1d0 1023) (scale-float 1d0 1023))
    
    668
    +	(complex 1d0 1d0)
    
    669
    +	(complex (scale-float 1d0 1023) 0)
    
    670
    +	53)
    
    671
    +  ;; 5
    
    672
    +  (frob cdiv.baudin-case.5.underflow-ratio
    
    673
    +	(complex (scale-float 1d0 1020) (scale-float 1d0 -844))
    
    674
    +	(complex (scale-float 1d0 656) (scale-float 1d0 -780))
    
    675
    +	(complex (scale-float 1d0 364) (scale-float -1d0 -1072))
    
    676
    +	53)
    
    677
    +  ;; 6
    
    678
    +  (frob cdiv.baudin-case.6.underflow-realpart
    
    679
    +	(complex (scale-float 1d0 -71) (scale-float 1d0 1021))
    
    680
    +	(complex (scale-float 1d0 1001) (scale-float 1d0 -323))
    
    681
    +	(complex (scale-float 1d0 -1072) (scale-float 1d0 20))
    
    682
    +	53)
    
    683
    +  ;; 7
    
    684
    +  (frob cdiv.baudin-case.7.overflow-both-parts
    
    685
    +	(complex (scale-float 1d0 -347) (scale-float 1d0 -54))
    
    686
    +	(complex (scale-float 1d0 -1037) (scale-float 1d0 -1058))
    
    687
    +	(complex 3.898125604559113300d289 8.174961907852353577d295)
    
    688
    +	53)
    
    689
    +  ;; 8
    
    690
    +  (frob cdiv.baudin-case.8
    
    691
    +	(complex (scale-float 1d0 -1074) (scale-float 1d0 -1074))
    
    692
    +	(complex (scale-float 1d0 -1073) (scale-float 1d0 -1074))
    
    693
    +	(complex 0.6d0 0.2d0)
    
    694
    +	53)
    
    695
    +  ;; 9
    
    696
    +  (frob cdiv.baudin-case.9
    
    697
    +	(complex (scale-float 1d0 1015) (scale-float 1d0 -989))
    
    698
    +	(complex (scale-float 1d0 1023) (scale-float 1d0 1023))
    
    699
    +	(complex 0.001953125d0 -0.001953125d0)
    
    700
    +	53)
    
    701
    +  ;; 10
    
    702
    +  (frob cdiv.baudin-case.10.improve-imagpart-accuracy
    
    703
    +	(complex (scale-float 1d0 -622) (scale-float 1d0 -1071))
    
    704
    +	(complex (scale-float 1d0 -343) (scale-float 1d0 -798))
    
    705
    +	(complex 1.02951151789360578d-84 6.97145987515076231d-220)
    
    706
    +	53)
    
    707
    +  ;; 11
    
    708
    +  ;;
    
    709
    +  ;; From Maxima.  This was from a (private) email where Maxima used
    
    710
    +  ;; CL:/ to compute the ratio but was not very accurate.
    
    711
    +  (frob cdiv.maxima-case
    
    712
    +	#c(5.43d-10 1.13d-100)
    
    713
    +	#c(1.2d-311 5.7d-312)
    
    714
    +	#c(3.691993880674614517999740937026568563794896024143749539711267954d301
    
    715
    +	   -1.753697093319947872394996242210428954266103103602859195409591583d301)
    
    716
    +	52)
    
    717
    +  ;; 12
    
    718
    +  ;;
    
    719
    +  ;; Found by ansi tests. z/z should be exactly 1.
    
    720
    +  (frob cdiv.ansi-test-z/z
    
    721
    +	#c(1.565640716292489d19 0.0d0)
    
    722
    +	#c(1.565640716292489d19 0.0d0)
    
    723
    +	#c(1d0 0)
    
    724
    +	53)
    
    725
    +  ;; 13
    
    726
    +  ;; Iteration 1.  Without this, we would instead return
    
    727
    +  ;;
    
    728
    +  ;;   (complex (parse-hex-float "0x1.ba8df8075bceep+155")
    
    729
    +  ;;            (parse-hex-float "-0x1.a4ad6329485f0p-895"))
    
    730
    +  ;;
    
    731
    +  ;; whose imaginary part is quite a bit off.
    
    732
    +  (frob cdiv.mcgehearty-iteration.1
    
    733
    +	(complex (parse-hex-float "0x1.73a3dac1d2f1fp+509")
    
    734
    +		 (parse-hex-float "-0x1.c4dba4ba1ee79p-620"))
    
    735
    +	(complex (parse-hex-float "0x1.adf526c249cf0p+353")
    
    736
    +		 (parse-hex-float "0x1.98b3fbc1677bbp-697"))
    
    737
    +	(complex (parse-hex-float "0x1.BA8DF8075BCEEp+155")
    
    738
    +		 (parse-hex-float "-0x1.A4AD628DA5B74p-895"))
    
    739
    +	53)
    
    740
    +  ;; 14
    
    741
    +  ;; Iteration 2.
    
    742
    +  (frob cdiv.mcgehearty-iteration.2
    
    743
    +	(complex (parse-hex-float "-0x0.000000008e4f8p-1022")
    
    744
    +		 (parse-hex-float "0x0.0000060366ba7p-1022"))
    
    745
    +	(complex (parse-hex-float "-0x1.605b467369526p-245")
    
    746
    +		 (parse-hex-float "0x1.417bd33105808p-256"))
    
    747
    +	(complex (parse-hex-float "0x1.cde593daa4ffep-810")
    
    748
    +		 (parse-hex-float "-0x1.179b9a63df6d3p-799"))
    
    749
    +	52)
    
    750
    +  ;; 15
    
    751
    +  ;; Iteration 3
    
    752
    +  (frob cdiv.mcgehearty-iteration.3
    
    753
    +	(complex (parse-hex-float "0x1.cb27eece7c585p-355 ")
    
    754
    +		 (parse-hex-float "0x0.000000223b8a8p-1022"))
    
    755
    +	(complex (parse-hex-float "-0x1.74e7ed2b9189fp-22")
    
    756
    +		 (parse-hex-float "0x1.3d80439e9a119p-731"))
    
    757
    +	(complex (parse-hex-float "-0x1.3b35ed806ae5ap-333")
    
    758
    +		 (parse-hex-float "-0x0.05e01bcbfd9f6p-1022"))
    
    759
    +	53)
    
    760
    +  ;; 16
    
    761
    +  ;; Iteration 4
    
    762
    +  (frob cdiv.mcgehearty-iteration.4
    
    763
    +	(complex (parse-hex-float "-0x1.f5c75c69829f0p-530")
    
    764
    +		 (parse-hex-float "-0x1.e73b1fde6b909p+316"))
    
    765
    +	(complex (parse-hex-float "-0x1.ff96c3957742bp+1023")
    
    766
    +		 (parse-hex-float "0x1.5bd78c9335899p+1021"))
    
    767
    +	(complex (parse-hex-float "-0x1.423c6ce00c73bp-710")
    
    768
    +		 (parse-hex-float "0x1.d9edcf45bcb0ep-708"))
    
    769
    +	52))
    
    770
    +
    
    771
    +(define-test complex-division.misc
    
    772
    +    (:tag :issue)
    
    773
    +  (let ((num '(1
    
    774
    +	       1/2
    
    775
    +	       1.0
    
    776
    +	       1d0
    
    777
    +	       #c(1 2)
    
    778
    +	       #c(1.0 2.0)
    
    779
    +	       #c(1d0 2d0)
    
    780
    +	       #c(1w0 2w0))))
    
    781
    +    ;; Try all combinations of divisions of different types.  This is
    
    782
    +    ;; primarily to test that we got all the numeric contagion cases
    
    783
    +    ;; for division in CL:/.
    
    784
    +    (dolist (x num)
    
    785
    +      (dolist (y num)
    
    786
    +	(assert-true (/ x y)
    
    787
    +		     x y)))))
    
    788
    +
    
    789
    +(define-test complex-division.single
    
    790
    +    (:tag :issues)
    
    791
    +  (let* ((x #c(1 2))
    
    792
    +	 (y (complex (expt 2 127) (expt 2 127)))
    
    793
    +	 (expected (coerce (/ x y)
    
    794
    +			   '(complex single-float))))
    
    795
    +    ;; A naive implementation of complex division would cause an
    
    796
    +    ;; overflow in computing the denominator.
    
    797
    +    (assert-equal expected
    
    798
    +		  (/ (coerce x '(complex single-float))
    
    799
    +		     (coerce y '(complex single-float)))
    
    800
    +		  x
    
    801
    +		  y)))