diff options
Diffstat (limited to 'compiler/saturate.nim')
-rw-r--r-- | compiler/saturate.nim | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/compiler/saturate.nim b/compiler/saturate.nim new file mode 100644 index 000000000..fe6e03c8b --- /dev/null +++ b/compiler/saturate.nim @@ -0,0 +1,79 @@ +# +# +# The Nim Compiler +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Saturated arithmetic routines. XXX Make part of the stdlib? + +proc `|+|`*(a, b: BiggestInt): BiggestInt = + ## saturated addition. + result = a +% b + if (result xor a) >= 0'i64 or (result xor b) >= 0'i64: + return result + if a < 0 or b < 0: + result = low(typeof(result)) + else: + result = high(typeof(result)) + +proc `|-|`*(a, b: BiggestInt): BiggestInt = + result = a -% b + if (result xor a) >= 0'i64 or (result xor not b) >= 0'i64: + return result + if b > 0: + result = low(typeof(result)) + else: + result = high(typeof(result)) + +proc `|abs|`*(a: BiggestInt): BiggestInt = + if a != low(typeof(a)): + if a >= 0: result = a + else: result = -a + else: + result = low(typeof(a)) + +proc `|div|`*(a, b: BiggestInt): BiggestInt = + # (0..5) div (0..4) == (0..5) div (1..4) == (0 div 4)..(5 div 1) + if b == 0'i64: + # make the same as ``div 1``: + result = a + elif a == low(typeof(a)) and b == -1'i64: + result = high(typeof(result)) + else: + result = a div b + +proc `|mod|`*(a, b: BiggestInt): BiggestInt = + if b == 0'i64: + result = a + else: + result = a mod b + +proc `|*|`*(a, b: BiggestInt): BiggestInt = + var + resAsFloat, floatProd: float64 + result = a *% b + floatProd = toBiggestFloat(a) # conversion + floatProd = floatProd * toBiggestFloat(b) + resAsFloat = toBiggestFloat(result) + + # Fast path for normal case: small multiplicands, and no info + # is lost in either method. + if resAsFloat == floatProd: return result + + # Somebody somewhere lost info. Close enough, or way off? Note + # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). + # The difference either is or isn't significant compared to the + # true value (of which floatProd is a good approximation). + + # abs(diff)/abs(prod) <= 1/32 iff + # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" + if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): + return result + + if floatProd >= 0.0: + result = high(typeof(result)) + else: + result = low(typeof(result)) |