Boost logo

Boost :

Subject: Re: [boost] [Safe Numerics] floating point conversions
From: John Maddock (jz.maddock_at_[hidden])
Date: 2017-03-12 11:49:54


>>
>> 3) If I assign a float to an integer, then I get the error: "conversion
>> of integer to float loses precision" which isn't quite what you meant to
>> say.
>> More particularly, for float conversions I would expect:
>>
>> * Conversion from a float holding an integer value that's within the
>> range of the target type to always succeed.
>> * Conversion from a float holding a non-integral value to conditionally
>> succeed (with trunction) depending on the policy in effect.
>> * Conversion *to* a float may also fail if the integer is outside the
>> range of the float (no checking may be required if the number of bits in
>> the integer is less than the number of bits in the float).
>
> my implementation of this is sort of an afterthought and needs
> attention. It's similar to the case as above. It's not clear what I
> want to do - even to me.
>
> int i = 1.0
>
> I wonder what we're really doing here. In C++/C i get it. But when I
> write in C++ the value 1.0 I'm mean a value which in some sense
> approximates 1.0. I think it's mistake.
>
> int j = 1.5
>
> This is pretty clear to me. The fundamental premise of the library
> would be stated.
>
> "All expressions which change the arithmetic value are considered errors"
>
> On the other hand, I might be OK with
>
> int k = floor(1.5);
>
> I'm still sorting out how I feel about going back and forth between
> floats and integers.
>

Nod.

It's fairly common to see in floating point code, something like:

int integer_part = my_float;
double fractional_part = my_float - integer_part;

which is of course pretty seriously unsafe :(

Casting to a safe<int> to ensure there is no overflow would simplify a
lot of error checking code.

Now we could insist that my_float is passed through
floor/ceil/trunc/round before the conversion, and that's OK by me even
though the function call is superfluous in this instance, but since we
don't know where a floating point value has been, we can't actually
allow that, without also allowing assignment from any old float (albeit
with runtime checking).

BTW for the record, Boost.Math uses itrunc/ltrunc/lltrunc for this
operation which throws when the float is too large for the target type.
Lots of ways to tackle this, I just wanted you to be aware that there
are genuine use cases where float-interoperability is useful and needed.

I guess there are several possible solutions:

Policies: we have 3 levels of checks:
* Permissive, allow all conversions from float and truncate (as long as
the truncated value is in range).
* Strict, only allow conversion from in-range integral values. Values
with fractional parts throw.
* Never: static assert on any attempt to convert from float.

An alternative is to use external functions, lets say we have:

template <class I, class F>
safe<I> safe_trunc(F f);

Which throws only when the truncated value is out of range. Likewise we
would need safe_round, safe_floor and safe_ceil. Obviously a certain
amount of "bike shed colouring" may be required on those names ;)

John.

---
This email has been checked for viruses by AVG.
http://www.avg.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk