Skip to content

Instantly share code, notes, and snippets.

@Araq
Created September 18, 2018 15:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Araq/c71b764b94188337b24c6180b239229d to your computer and use it in GitHub Desktop.
Save Araq/c71b764b94188337b24c6180b239229d to your computer and use it in GitHub Desktop.
barebones decimal number implementation for Nim
## Decimal data representation
## (c) Andreas Rumpf
from strutils import intToStr
type
Decimal* = object ## Represents a decimal value.
dv: int32
val: int64
proc toDecimal*(intPart: int, fractionPart=0; decimalPlaces=2): Decimal =
## General constructor.
if decimalPlaces == 2:
result.dv = 100
elif decimalPlaces == 4:
result.dv = 10_000
else:
var dv = 1
for i in 1..decimalPlaces: dv *= 10
result.dv = dv.int32
result.val = intPart * result.dv + fractionPart
proc m0*(x: int): Decimal =
## Constructor for Decimals with 1 decimal place.
Decimal(dv: 10, val: x*10)
proc m00*(x: int): Decimal =
## Constructor for Decimals with 2 decimal places.
Decimal(dv: 100, val: x*100)
proc m000*(x: int): Decimal =
## Constructor for Decimals with 3 decimal places.
Decimal(dv: 1000, val: x*1000)
proc m0000*(x: int): Decimal =
## Constructor for Decimals with 4 decimal places.
Decimal(dv: 10_000, val: x*10_000)
proc decimalPlaces*(x: Decimal): int =
result = 0
var dv = x.dv
while dv >= 10:
dv = dv div 10
inc result
proc `$`*(a: Decimal; sep='.'): string =
result = $(a.val div a.dv) & sep & intToStr(int(a.val mod a.dv), decimalPlaces(a))
proc convert(a, b: Decimal): int64 =
## convert a's to b's representation
# a = 10, b = 1000 --> diff = 100
assert b.dv > a.dv
let diff = b.dv div a.dv
assert b.dv mod a.dv == 0
result = a.val * diff
template plusOp(op: untyped) =
proc op*(a, b: Decimal): Decimal =
if a.dv == b.dv:
result = Decimal(dv: a.dv, val: op(a.val, b.val))
elif a.dv > b.dv:
# convert b to a's representation
result = Decimal(dv: a.dv, val: op(a.val, convert(b, a)))
else:
# convert a to b's representation
result = Decimal(dv: b.dv, val: op(convert(a, b), b.val))
template mulOp(op: untyped) =
proc op*(a: Decimal; b: int64): Decimal =
Decimal(dv: a.dv, val: op(a.val, b))
template unOp(op: untyped) =
proc op*(a: Decimal): Decimal = Decimal(dv: a.dv, val: op(a.val))
plusOp(`+`)
plusOp(`-`)
mulOp(`*`)
mulOp(`div`)
mulOp(`mod`)
unOp(`-`)
proc sum*(x: openArray[Decimal]): Decimal =
result = x[0]
for i in 1..<x.len:
result = result + x[i]
proc avg*(x: openArray[Decimal]): Decimal =
sum(x) div x.len
when isMainModule:
template test(x, y) =
echo x
doAssert $x == y
test 55.m00, "55.00"
test 55.m0 + 6.m000, "61.000"
test 3.m0 * 5, "15.0"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment