@@ -500,35 +500,45 @@ public final class MathLibrary: NativeLibrary {
500500 }
501501 }
502502
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)
507508 } else {
508- return . makeNumber ( MathLibrary . approximateBigRat ( x ) )
509+ return . false
509510 }
510511 }
511512
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 > ? {
513514 let mx = x * tolerance
514515 var y = x
515516 var ( n1, d1) = ( Int64 ( 1 ) , Int64 ( 0 ) )
516517 var ( n2, d2) = ( Int64 ( 0 ) , Int64 ( 1 ) )
517518 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+ }
522529 } while abs ( x - Double( n1) / Double( d1) ) > mx
523530 return Rational ( n1, d1)
524531 }
525532
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 > ? {
527534 let mx = x * tolerance
528535 var y = x
529536 var ( n1, d1) = ( BigInt ( 1 ) , BigInt ( 0 ) )
530537 var ( n2, d2) = ( BigInt ( 0 ) , BigInt ( 1 ) )
531538 repeat {
539+ guard y. isFinite else {
540+ return nil
541+ }
532542 let fy = BigInt ( Foundation . floor ( y) )
533543 ( n1, n2) = ( fy * n1 + n2, n1)
534544 ( d1, d2) = ( fy * d1 + d2, d1)
@@ -547,7 +557,14 @@ public final class MathLibrary: NativeLibrary {
547557 case . rational( . fixnum( let n) , . fixnum( let d) ) :
548558 return . makeNumber( Int64 ( Foundation . floor ( Double ( n) / Double( d) ) ) )
549559 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+ }
551568 case . flonum( let num) :
552569 return . makeNumber( Foundation . floor ( num) )
553570 default :
@@ -562,7 +579,12 @@ public final class MathLibrary: NativeLibrary {
562579 case . rational( . fixnum( let n) , . fixnum( let d) ) :
563580 return . makeNumber( Int64 ( Foundation . ceil ( Double ( n) / Double( d) ) ) )
564581 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+ }
566588 case . flonum( let num) :
567589 return . makeNumber( ceil ( num) )
568590 default :
@@ -577,7 +599,7 @@ public final class MathLibrary: NativeLibrary {
577599 case . rational( . fixnum( let n) , . fixnum( let d) ) :
578600 return . makeNumber( Int64 ( Foundation . trunc ( Double ( n) / Double( d) ) ) )
579601 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 )
581603 case . flonum( let num) :
582604 return . makeNumber( trunc ( num) )
583605 default :
@@ -587,14 +609,40 @@ public final class MathLibrary: NativeLibrary {
587609
588610 private func round( _ expr: Expr ) throws -> Expr {
589611 switch expr {
590- case . fixnum( _) , . bignum( _) :
612+ case . fixnum( _) ,
613+ . bignum( _) :
591614 return expr
592615 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 ) ) )
594617 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+ }
596644 case . flonum( let num) :
597- return . makeNumber( Foundation . round ( num ) )
645+ return . makeNumber( num . rounded ( . toNearestOrEven ) )
598646 default :
599647 throw RuntimeError . type ( expr, expected: [ . realType] )
600648 }
0 commit comments