@@ -500,35 +500,45 @@ public final class MathLibrary: NativeLibrary {
500
500
}
501
501
}
502
502
503
- static func approximateNumber( _ x: Double , tolerance: Double = 1.0e-16 ) -> Expr {
504
- let max = Double ( Int64 . max)
505
- if x > - max && x < max {
506
- return . makeNumber( MathLibrary . approximate ( x) )
503
+ static func approximateNumber( _ x: Double , tolerance: Double = 1.0e-15 ) -> Expr {
504
+ if let rat = MathLibrary . approximate ( x, tolerance: tolerance) {
505
+ return . makeNumber( rat)
506
+ } else if let rat = MathLibrary . approximateBigRat ( x, tolerance: tolerance) {
507
+ return . makeNumber( rat)
507
508
} else {
508
- return . makeNumber ( MathLibrary . approximateBigRat ( x ) )
509
+ return . false
509
510
}
510
511
}
511
512
512
- static func approximate( _ x: Double , tolerance: Double = 1.0e-16 ) -> Rational < Int64 > {
513
+ static func approximate( _ x: Double , tolerance: Double = 1.0e-15 ) -> Rational < Int64 > ? {
513
514
let mx = x * tolerance
514
515
var y = x
515
516
var ( n1, d1) = ( Int64 ( 1 ) , Int64 ( 0 ) )
516
517
var ( n2, d2) = ( Int64 ( 0 ) , Int64 ( 1 ) )
517
518
repeat {
518
- let fy = Int64 ( Foundation . floor ( y) )
519
- ( n1, n2) = ( fy * n1 + n2, n1)
520
- ( d1, d2) = ( fy * d1 + d2, d1)
521
- y = 1.0 / ( y - Foundation. floor ( y) )
519
+ guard y. isFinite else {
520
+ return nil
521
+ }
522
+ if let fy = Int64 ( exactly: Foundation . floor ( y) ) {
523
+ ( n1, n2) = ( fy * n1 + n2, n1)
524
+ ( d1, d2) = ( fy * d1 + d2, d1)
525
+ y = 1.0 / ( y - Foundation. floor ( y) )
526
+ } else {
527
+ return nil
528
+ }
522
529
} while abs ( x - Double( n1) / Double( d1) ) > mx
523
530
return Rational ( n1, d1)
524
531
}
525
532
526
- static func approximateBigRat( _ x: Double , tolerance: Double = 1.0e-16 ) -> Rational < BigInt > {
533
+ static func approximateBigRat( _ x: Double , tolerance: Double = 1.0e-15 ) -> Rational < BigInt > ? {
527
534
let mx = x * tolerance
528
535
var y = x
529
536
var ( n1, d1) = ( BigInt ( 1 ) , BigInt ( 0 ) )
530
537
var ( n2, d2) = ( BigInt ( 0 ) , BigInt ( 1 ) )
531
538
repeat {
539
+ guard y. isFinite else {
540
+ return nil
541
+ }
532
542
let fy = BigInt ( Foundation . floor ( y) )
533
543
( n1, n2) = ( fy * n1 + n2, n1)
534
544
( d1, d2) = ( fy * d1 + d2, d1)
@@ -547,7 +557,14 @@ public final class MathLibrary: NativeLibrary {
547
557
case . rational( . fixnum( let n) , . fixnum( let d) ) :
548
558
return . makeNumber( Int64 ( Foundation . floor ( Double ( n) / Double( d) ) ) )
549
559
case . rational( . bignum( let n) , . bignum( let d) ) :
550
- return . makeNumber( Int64 ( Foundation . floor ( n. doubleValue / d. doubleValue) ) )
560
+ let ( quotient, remainder) = n. divided ( by: d)
561
+ if remainder. isZero {
562
+ return . makeNumber( quotient)
563
+ } else if quotient. isNegative {
564
+ return . makeNumber( quotient. minus ( 1 ) )
565
+ } else {
566
+ return . makeNumber( quotient)
567
+ }
551
568
case . flonum( let num) :
552
569
return . makeNumber( Foundation . floor ( num) )
553
570
default :
@@ -562,7 +579,12 @@ public final class MathLibrary: NativeLibrary {
562
579
case . rational( . fixnum( let n) , . fixnum( let d) ) :
563
580
return . makeNumber( Int64 ( Foundation . ceil ( Double ( n) / Double( d) ) ) )
564
581
case . rational( . bignum( let n) , . bignum( let d) ) :
565
- return . makeNumber( Int64 ( Foundation . ceil ( n. doubleValue / d. doubleValue) ) )
582
+ let ( quotient, remainder) = n. divided ( by: d)
583
+ if remainder. isZero || quotient. isNegative {
584
+ return . makeNumber( quotient)
585
+ } else {
586
+ return . makeNumber( quotient. plus ( 1 ) )
587
+ }
566
588
case . flonum( let num) :
567
589
return . makeNumber( ceil ( num) )
568
590
default :
@@ -577,7 +599,7 @@ public final class MathLibrary: NativeLibrary {
577
599
case . rational( . fixnum( let n) , . fixnum( let d) ) :
578
600
return . makeNumber( Int64 ( Foundation . trunc ( Double ( n) / Double( d) ) ) )
579
601
case . rational( . bignum( let n) , . bignum( let d) ) :
580
- return . makeNumber( Int64 ( Foundation . trunc ( n . doubleValue / d . doubleValue ) ) )
602
+ return . makeNumber( n . divided ( by : d ) . quotient )
581
603
case . flonum( let num) :
582
604
return . makeNumber( trunc ( num) )
583
605
default :
@@ -587,14 +609,40 @@ public final class MathLibrary: NativeLibrary {
587
609
588
610
private func round( _ expr: Expr ) throws -> Expr {
589
611
switch expr {
590
- case . fixnum( _) , . bignum( _) :
612
+ case . fixnum( _) ,
613
+ . bignum( _) :
591
614
return expr
592
615
case . rational( . fixnum( let n) , . fixnum( let d) ) :
593
- return . makeNumber( Int64 ( Foundation . round ( Double ( n) / Double( d) ) ) )
616
+ return . makeNumber( Int64 ( ( Double ( n) / Double( d) ) . rounded ( . toNearestOrEven ) ) )
594
617
case . rational( . bignum( let n) , . bignum( let d) ) :
595
- return . makeNumber( Int64 ( Foundation . round ( n. doubleValue / d. doubleValue) ) )
618
+ let ( quotient, remainder) = n. divided ( by: d)
619
+ if remainder. isZero {
620
+ return . makeNumber( quotient)
621
+ } else {
622
+ let doubleQuotient = remainder. abs. times ( 2 )
623
+ let denom = d. abs
624
+ if doubleQuotient == denom {
625
+ if quotient. isOdd {
626
+ if quotient. isNegative {
627
+ return . makeNumber( quotient. minus ( 1 ) )
628
+ } else {
629
+ return . makeNumber( quotient. plus ( 1 ) )
630
+ }
631
+ } else {
632
+ return . makeNumber( quotient)
633
+ }
634
+ } else if doubleQuotient > denom {
635
+ if quotient. isNegative {
636
+ return . makeNumber( quotient. minus ( 1 ) )
637
+ } else {
638
+ return . makeNumber( quotient. plus ( 1 ) )
639
+ }
640
+ } else {
641
+ return . makeNumber( quotient)
642
+ }
643
+ }
596
644
case . flonum( let num) :
597
- return . makeNumber( Foundation . round ( num ) )
645
+ return . makeNumber( num . rounded ( . toNearestOrEven ) )
598
646
default :
599
647
throw RuntimeError . type ( expr, expected: [ . realType] )
600
648
}
0 commit comments