Boost :

Subject: Re: [boost] [Boost.FixedPoint] Choose notation
From: Michael Marcin (mike.marcin_at_[hidden])
Date: 2013-04-30 12:52:32

On 4/30/2013 1:13 AM, Vicente J. Botet Escriba wrote:
> Le 29/04/13 20:56, Michael Marcin a écrit :
>> On 4/29/2013 12:18 PM, Vicente Botet wrote:
>>> No, this is not solved by implicit/explicit conversion, but using closed
>>> arithmetic. That is
>>>
>>> fixed<8>* fixed<8>-> fixed<8>
>>>
>>> The C++1y proposal uses open arithmetic and
>>>
>>> fixed<8>* fixed<8>-> fixed<16>
>>>
>>> Using closed arithmetic the user must explicitly convert to the desired
>>> result
>>>
>>> fixed<8> a;
>>> fixed<16> b = fixed<16>(a) * fixed<16>(a);
>>
>> Whenever possible fixed-point multiply is done in a larger temporary
>> then scaled back down. If you can't use a larger internal type it
>> becomes much more complicated and takes many more instructions to do
>> the operation.
>>
>> I don't see how this explicit conversion of the operands helps. You've
>> gone from needing a 32bit internal type to a 64bit internal type.
>>
>> If you promote on multiply things get out of hand pretty quickly when
>> you chain operations writing natural code.
>>
>> fixed<16,16> a;
>> auto b = a*a*a*a;
>>
>> b is a 128bit fixed<64,64>
>>
>> There is a middle ground solution whereby:
>> a*a -> promoted_fixed<32,32>
>>
>> so a*a*a*a would be:
>> fixed<16,16> * fixed<16,16> -> promoted_fixed<32,32>
>> promoted_fixed<32,32> * fixed<16,16> -> promoted_fixed<32,32>
>> promoted_fixed<32,32> * fixed<16,16> -> promoted_fixed<32,32>
>>
> IIUC, your design is there to solve the usual case of having 2
> arguments, but IMO don't works when there are multiple arguments as you
> can loss information here.
> Why do you find it is safe to do that?
> Why this is better and how it solves the issue?
>

I don't know about safe, it's not "safe" to do that on any built-in type
in the language.

This is basically just the difference between our use
cases/requirements. I want math that can be efficient on the system. If
the system had a FPU I would most likely just be using float. You need
accuracy and lossless computation.

I choose to use only types that can be used efficiently on the system.
In this example I'm assuming a system (like many I've worked on) that
has only limited 64-bit type support and no 96 or 128bit types.

In this case I want to do the best I can with needed big-int style types.

If I could take the lossless interface and write this code even a bit
messier it'd be a non-issue.

The best I can come up with is
a*a*a*a
into
typedef fixed<16,16,int32_t> real_t;
typedef fixed<16,16,int64_t> promoted_t;
real_t a;
auto b = real_t(promoted(promoted(a*a)*a)*a);

If this works as I expect it might be enough.

>> Then do the scaling down only at the end.
>> fixed<16,16> b = a*a*a*a;
> This would not compile on my prototype with open arithmetic and on C++1y
> proposal as there is a possible loss of information. An explicit
> downcast is needed.
>> Unfortunately auto no longer does the intuitive thing here, but there
>> is precedence for that in the language.
>>
> Could you give us some examples?
>

auto i1 = 10; // i1 is int
auto i2(10); // i2 is int
auto i3 {10}; // i3 is std::initializer_list<int>