aopp-pred / rpe

An emulator for reduced floating-point precision written in Fortran.
http://rpe.readthedocs.io/
Apache License 2.0
9 stars 8 forks source link

Add `rpe_literal` constructor functions. #6

Closed ajdawson closed 8 years ago

ajdawson commented 8 years ago

Sometimes you want to perform a reduced-precision calculation that involves a real literal constant (something not defined as a variable), and currently this could cause the precision of intermediate steps to be increased.

A simple example:

type(rpe_var) :: a, b, r

! Work with 10 bits precision by default.
RPE_DEFAULT_SBITS = 10

a = 1.234
b = 5.678
r = 0.9 * a + b

Because of casting between mixed precision types the last calculation will be evaluated retaining 23 bits in the significand (the intermediate tmp1 = 0.9 * a is stored at 23 bits because 0.9 is a real number with a 23-bit significand, and tmp1 + b is also evaluated with 23 bits of precision because tmp1 has 23 bits in its significand).

This PR adds a new interface rpe_var which is a quick and convenient way of generating one-shot reduced-precision variables from a literal. Using it the example above would become:

type(rpe_var) :: a, b, r

! Work with 10 bits precision by default.
RPE_DEFAULT_SBITS = 10

a = 1.234
b = 5.678
r = rpe_literal(0.9) * a + b

The function rpe_literal(value) call takes a literal input and converts it to an rpe_var instance with the default number of significand bits, in the example above 10 (i.e. the value is truncated to 10-bits in the significand and returned as an rpe_type). Optionally you can pass a number of bits as in:

type(rpe_var) :: a, b, r

! Work with 10 bits precision by default.
RPE_DEFAULT_SBITS = 10

a = 1.234
b = 5.678
r = rpe_literal(0.9, 5) * a + b

which would only use 5 significand bits to represent the literal 0.9.

Thoughts and feedback on both usefulness and implementation welcome.

ajdawson commented 8 years ago

Ping @dueben @samhatfield.

ajdawson commented 8 years ago

I've updated this branch. The main change is the use of a constructor interface instead of rpe_literal. I feel this is a closer representation of what the interface does. I've also added tests and updated the documentation.

ajdawson commented 8 years ago

@dueben - can you review this when you get a chance please?

samhatfield commented 8 years ago

So I understand, in this example:

type(rpe_var) :: a, b, r

! Work with 10 bits precision by default.
RPE_DEFAULT_SBITS = 10

a = 1.234
b = 5.678
r = 0.9 * a + b

significand_bits(r) == 10 still, no? Even though the two intermediate calculations are performed in full precision, the full significand of 0.9 doesn't 'cross' the equals sign?

ajdawson commented 8 years ago

Correct, assignments always truncate to the number of bits the variable on the LHS has. This is a simplified example which probably wouldn't suffer too badly. However, we have has cases "in the wild" where this situation has caused big problems.

ajdawson commented 8 years ago

After offline discussion, I've changed back to the name rpe_literal as it fits better with the intended usage scope, otherwise as it was.

ajdawson commented 8 years ago

Any last issues or objections @samhatfield @dueben?

dueben commented 8 years ago

Looks good to me!