Powershell Midpoint Rounding

87 Views Asked by At

I am building an API and the system that I am using just uses midpoint rounding in PowerShell. I've come to find out that certain numbers still disobey this rule and my research as to why has proven futile. Basically, anything below 5 rounds down, and anything above 5 rounds up, except for certain situations I guess?

My scripts are included in this image if anybody would like to assist.

My Scripts

11.025 = 11.03
12.025 = 12.03
29.025 = 29.03
39.025 = 39.03

This values and everything in the script is expected EXCEPT for:

19.025 = 19.02

If somebody could explain what is going on that would be greatly appreciated.

2

There are 2 best solutions below

1
On

I am using [float] variables, everything I have seen about rounding issues has been about [float] variables. I casted to [decimal] instead, and that cleared everything up. I guess there are bits in a [float] that sometimes prohibit it from rounding with MidpointRounding.

1
On

In PowerShell floating-point literals are of type [double], and the closest to 19.025 in double precision is 19.0249999999999985789... which is closer to 19.02 than 19.03. Therefore the result is as expected.

In fact most decimal values won't be stored exactly in binary floating-point. If you're not aware of that please check out Is floating point math broken?. It's impossible to store your values as finite non-repeating binary fractional values. You can check that easily by printing more digits

PS $> 11.025, 12.025, 19.015, 19.025, 29.025, 39.025 |% { $_.ToString("G60") }
11.0250000000000003552713678800500929355621337890625
12.0250000000000003552713678800500929355621337890625
19.0150000000000005684341886080801486968994140625
19.02499999999999857891452847979962825775146484375
29.02499999999999857891452847979962825775146484375
39.02499999999999857891452847979962825775146484375

There is nothing that prevents midpoint rounding from working, it's just because the digit isn't exactly 5. If you want to deal with exact decimal values you must use the [decimal] type. But you'll have to initialize the value directly with the d suffix (like 19.025d) or convert from string as [decimal]"19.025". Don't cast from [double] to [decimal] because that won't give you the lost precision back, [decimal]double_literal is done as [decimal]([double]double_literal). [decimal]19.025 can result in the value rounded to [double] which is 19.0249999999999985789... Somehow it works in that case, probably because the shell constructs the [decimal] directly from the literal as string, but it won't work in all cases, for example

PS $> [decimal]19.0255555555555555555555555555
19.0255555555556
PS $> [decimal]19.02555555555555e25
190255555555556000000000000