hidden text to trigger early load of fonts ПродукцияПродукцияПродукцияПродукция Các sản phẩmCác sản phẩmCác sản phẩmCác sản phẩm المنتجاتالمنتجاتالمنتجاتالمنتجات מוצריםמוצריםמוצריםמוצרים
Nios® V/II Embedded Design Suite (EDS)
Support for Embedded Development Tools, Processors (SoCs and Nios® V/II processor), Embedded Development Suites (EDSs), Boot and Configuration, Operating Systems, C and C++
12686 討論

Double precision floating point math problem with NiosII/e

Altera_Forum
榮譽貢獻者 II
3,039 檢視

I'm planning to use budget NiosII/e, provided by SOPC with Cyclone III. 

 

I wrote a simple program, to check if its double floating point math works correctly: 

 

#include <stdio.h> 

#include <math.h> 

 

#define PI 3.1415926535 

#define RANGE 10000 

 

int i; double s, t; 

 

 

 

int main ( void ) {// Calculating sum s 

s = 0;  

for (i = 0; i < RANGE; i++) s += PI*i; 

// Directly calculating value t which is equal to s 

t = PI*RANGE*(RANGE-1)/2.0; 

 

 

printf ("s = %e\n", s);  

printf ("t = %e\n", t);  

printf ("|s-t|= %f\n", fabs(s-t)); 

printf ("Rel err = %e\n", 2.0*fabs(s-t)/fabs(s+t)); 

return 0; 

 

Case 1:  

Hardware: default niosii/e processor without custom floating point hardware instruction was generated by SOPC. The program will print the following: 

s = 1.570639e+08 

t = 1.570639e+08 

|s-t|= 0.000000 

Rel err = 0.000000e+00 

 

Case 2: 

Default NiosII/e processor with custom floating point hardware instruction was generated by SOPC. The program will print the following: 

 

s = 1.570639e+08 

t = 1.570639e+08 

|s-t|= 6.931729 

Rel err = 4.413317e-08 

 

This is definitely not the double precision. 

Why compiler generates the code which gives incorrect result? 

 

Case 3: If I select Case 2 with custom f/p hardware instruction but disable floating point hardware by compiler# pragma directives.  

 

#pragma no_custom_fadds 

#pragma no_custom_fsubs 

#pragma no_custom_fmuls 

#pragma no_custom_fdivs 

 

#pragma no_custom_faddd 

#pragma no_custom_fsubd 

#pragma no_custom_fmuld 

#pragma no_custom_fdivd 

 

This should force software emulation of floating point, but the result doesn't change (As in Case 2): 

 

s = 1.570639e+08 

t = 1.570639e+08 

|s-t|= 6.931729 

Rel err = 4.413317e-08 

 

 

Why I cannot disable custom instruction and obtain the same double precision result as in Case 1? 

 

Can this strange behaviour explained/corrected/predicted?  

Can NiosII/e (with floating point hardware) be utilised for double precision calculation? 

0 積分
8 回應
Altera_Forum
榮譽貢獻者 II
1,803 檢視

Hello, 

 

You can find some answers in the tutorial "Using Nios II Floating-Point Custom Instructions" (http://www.altera.com/literature/tt/tt_floating_point_custom_instructions.pdf (http://www.altera.com/literature/tt/tt_floating_point_custom_instructions.pdf%29)). 

 

"The floating-point custom instructions, implement single precision floating-point arithmetic operations." So you cannot use floating point hardware for double precision calculation, whatever the type of processor (e/s/f). 

 

For your case 3, I agree that if you disable it with pragma directives it should use software implementation as in case 1, but I don't know the second pragma directives you use (which finished with a 'd'), where do you find them. On the tutorial they put only the first ones (which finished with a 's'). 

 

Jérôme
Altera_Forum
榮譽貢獻者 II
1,803 檢視

Hi Jerome, 

thank you for your reply. 

 

 

--- Quote Start ---  

Hello, 

You can find some answers in the tutorial "Using Nios II Floating-Point Custom Instructions" 

... 

So you cannot use floating point hardware for double precision calculation, whatever the type of processor (e/s/f). 

 

--- Quote End ---  

 

Yes I looked in that - it is cleary written. you are right about hardware precision, but in fact it is possible to do double floating point math if no fp floatig point custom instruction hardware is used with Nios/e. Below I wrote one more program, which proves that.  

i want to keep valuable fp custom instruction hardware configuration but to find a solution whether it can be temporary disabled on demand in order to do precision-sensitive calculations.  

 

 

--- Quote Start ---  

 

For your case 3, I agree that if you disable it with pragma directives it should use software implementation as in case 1, but I don't know the second pragma directives you use (which finished with a 'd'), where do you find them. On their tutorial they put only the first ones (which finished with a 's'). 

 

--- Quote End ---  

 

This is what made me curious. I decided to use fp custom instruction hardware but disable it when I need software double precision calculation. 

 

About directives - that might be not very nice from me but I scanned some executable files for the possible "no_custom" text in them and found that compiler might supports both types of directives: 

# pragma no_custom_fxxxs // no custom xxx float single ? As in tutorial 

and # pragma no_custom_fxxxd // no custom xxx float double ? I supposed 

 

Therefore I tried both directives. Still I don't know if it is possible to get pure double precision (however software emulated) with Nios2+custom fp instruction present but disabled.  

 

With Regards, 

Sergei. 

-------------- Double precision math proof ---------------- 

the program  

 

#include <stdio.h> 

 

#include <math.h> 

#include <float.h> 

// Single precision variables 

 

// 15 digits double precision constants 

 

float as = 1.000000000000100; 

float bs = 1.000000000000055; 

// Double precision variables 

 

// 15 digits double precision constants 

 

double ad = 1.000000000000100; 

double bd = 1.000000000000055; 

#define no_custom_fadds 

#define no_custom_fsubs 

#define no_custom_fmuls 

#define no_custom_fdivs 

 

 

int main (void) { 

 

printf ("---------- single precision (float) -----------\n"); 

printf ("sizeof(a) = %lu bytes\n", sizeof(as)); 

printf ("single: a = %.15f\n", as); 

printf ("single: b = %.15f\n", bs); 

printf ("single: a-b = %.15f\n", as-bs); 

// Since a = 1+x, b = 1+y, where x,y << 1 

// sqrt(a) = sqrt (1+x) ~ 1+x/2, sqrt(b) = sqrt (1+y) ~ 1+y/2 

// sqrt(a)*sqrt(b) ~ (1+x/2)*(1+y/2) ~ (1+(x+y)/2) = (a+b)/2 

printf ("sqrt(a)*sqrt(b) = %.15f\n", sqrt(as)*sqrt(bs)); 

printf ("(a+b)/2 = %.15f\n", (as+bs)/2); 

 

printf ("---------- double precision (double) -----------\n"); 

printf ("sizeof(a) = %lu bytes\n", sizeof(ad)); 

printf ("double: a = %.15f\n", ad); 

printf ("double: b = %.15f\n", bd); 

printf ("double: a-b = %.15f\n", ad-bd); 

// Since a = 1+x, b = 1+y, where x,y << 1 

// sqrt(a) = sqrt (1+x) ~ 1+x/2, sqrt(b) = sqrt (1+y) ~ 1+y/2 

// sqrt(a)*sqrt(b) ~ (1+x/2)*(1+y/2) ~ (1+(x+y)/2) = (a+b)/2 

printf ("sqrt(a)*sqrt(b) = %.15f\n", sqrt(ad)*sqrt(bd)); 

printf ("(a+b)/2 = %.15f\n", (ad+bd)/2); 

 

printf ("---------- C/C++ compiler constants ------------\n"); 

printf ("Maximum float value %e\n", FLT_MAX); 

printf ("Maximum double value %e\n", DBL_MAX); 

 

while (1); 

return 0; 

 

 

 

[/B][/B]case a: nios2/e no fp instruction: program prints: 

 

---------- single precision (float) ----------- 

sizeof(a) = 4 bytes 

single: a = 1.000000000000000 

single: b = 1.000000000000000 

single: a-b = 0.000000000000000 

sqrt(a)*sqrt(b) = 1.000000000000000 

(a+b)/2 = 1.000000000000000 

---------- double precision (double) ----------- 

sizeof(a) = 8 bytes 

double: a = 1.000000000000100 

double: b = 1.000000000000055 

double: a-b = 0.000000000000045 

sqrt(a)*sqrt(b) = 1.000000000000077 

(a+b)/2 = 1.000000000000077 

---------- C/C++ compiler constants ------------ 

Maximum float value 3.402823e+38 

Maximum double value 1.797693e+308 

 

case b: nios2/e with fp instruction: program prints: 

---------- single precision (float) ----------- 

sizeof(a) = 4 bytes 

single: a = 1.000000000000000 

single: b = 1.000000000000000 

single: a-b = 0.000000000000000 

sqrt(a)*sqrt(b) = 1.000000000000000 

(a+b)/2 = 1.000000000000000 

---------- double precision (double) ----------- 

sizeof(a) = 8 bytes 

double: a = 1.000000000000000 

double: b = 1.000000000000000 

double: a-b = 0.000000000000000 

sqrt(a)*sqrt(b) = 1.000000000000000 

(a+b)/2 = 1.000000000000000 

---------- C/C++ compiler constants ------------ 

Maximum float value 3.402823e+38 

Maximum double value inf
Altera_Forum
榮譽貢獻者 II
1,803 檢視

Check the objdump file and look at the floating point operators to see if hardware is being called (you should see the instruction "custom" instead of a software library call if the hardware is being used). It almost appears like somehow single precision hardware is being used for double precision operators which shouldn't be the case.

Altera_Forum
榮譽貢獻者 II
1,802 檢視

Hello, 

 

 

--- Quote Start ---  

Yes I looked in that - it is cleary written. you are right about hardware precision, but in fact it is possible to do double floating point math if no fp floatig point custom instruction hardware is used with Nios/e 

 

--- Quote End ---  

Yes, the type of processor (e/s/f) does not change the behaviour related to float/double operations (at least I don't see it anywhere). Do you get the same results with all the type of processor ? 

 

Else after reading again the doc, I find that your results are quiet strange. In Nios II Processor Reference Handbook, they say : 

 

 

--- Quote Start ---  

 

All the floating-point custom instructions are single-precision operations. Double-precision operations are implemented in software. By default, the Nios II compiler treats floating-point constants as double-precision numbers. To use the floating-point custom instructions for operations with floating-point constants, append an “f” to the constant. This tells the compiler to treat the constant as a single-precision floating-point value, rather than promote the other variables in your equation to double precision numbers. 

 

| Example Code | Precision | Floating-Point Custom Instruction Usage | 

| y = x × 4.67; | Double | No | 

| y = x × 4.67f; | Single | Yes | 

 

--- Quote End ---  

In your program, your variables and your constants are all double, so even if you add the floating point custom instruction (as in your case 2 and B), it should not be use it and all the calculus should be in double precision.
Altera_Forum
榮譽貢獻者 II
1,803 檢視

I think I might know what's going on here. The double precision constants are being treated as single precision constants. When you include the custom instruction you get one of the following flags passed in: 

 

-mcustom-fpu-config=60-1 (+, -, *) 

-mcustom-fpu-config=60-2 (+, -, *, /) 

 

These two flags boil down the the following: 

 

-mcustom-fmuls=252 –mcustom-fadds=253 –mcustom-fsubs=254 -fsingle-precision-constant 

 

 

-mcustom-fmuls=252 –mcustom-fadds=253 –mcustom-fsubs=254 –mcustom-divs=255 -fsingle-precision-constant 

 

The workaround would be to just manually pass these flags in for custom instructions 252-254/255 leaving out "-fsingle-precision-constant". If you use the software build tools newlib will be recompiled when you pass these flags in to include hardware support. The key thing though is that without -fsingle-prcecision-constant you should no longer see double precision constants trimmed down to single precision resolution.
Altera_Forum
榮譽貢獻者 II
1,803 檢視

 

--- Quote Start ---  

I think I might know what's going on here. The double precision constants are being treated as single precision constants.  

... 

 

--- Quote End ---  

 

Yes - this is the reason. Thanks a lot for that! 

 

I found one more workaround with constants - they must be marked with 'L' - suffix as long double. Long double constant will be converted properly into double. 

 

I wrote one more simple program, 

 

#include <stdio.h> 

/* 

#pragma no_custom_fadds 

#pragma no_custom_fsubs 

#pragma no_custom_fmuls 

#pragma no_custom_fdivs 

*/ 

 

double a, b, c; 

 

 

 

int main( void ) { 

 

 

// Long double constant will be converted properly into double 

a = 1.000000000000011L; 

b = 1.000000000000004L; 

c = 1e-7L; 

printf ("a = %.15f\n", a); 

printf ("b = %.15f\n", b); 

printf ("c = %.15f\n", c); 

printf ("a+b+c*c = %.15f\n", a+b+c*c); // prints correct double precision result 

 

while (1); 

return 0; 

 

result: 

a = 1.000000000000011 

b = 1.000000000000004 

c = 0.000000100000000 

a+b+c*c = 2.000000000000025 

Altera_Forum
榮譽貢獻者 II
1,803 檢視

 

--- Quote Start ---  

 

 

--- Quote Start ---  

 

by default, the nios ii compiler treats floating-point constants as double-precision numbers. To use the floating-point custom instructions for operations with floating-point constants, append an “f” to the constant. 

 

--- Quote End ---  

 

In your program, your variables and your constants are all double, so even if you add the floating point custom instruction (as in your case 2 and B), it should not be use it and all the calculus should be in double precision. 

--- Quote End ---  

 

Now the rules are following: 

 

1. If custom fp instruction is added to Nios hardware, then compiler treats default constants as single precision, regardless disabling pragma directives. 

 

2. If no custom fp instruction is present to Nios hardware, then compiler treats default constants as double precision. 

 

3. In any case we can explicitely use suffixes 'F' or 'L to specify the constant precision. that will ensure that code generation is correct regardless nios implementation. 

 

Thanks to everyone.
Altera_Forum
榮譽貢獻者 II
1,802 檢視

That's correct. I'm asking around to see if there is a less cumbersome way to workaround this issue. It all boils down to disabling the passing of the -mcustom-fpu-config flags automatically and just passing in the individual flags for binding to the multipiler, adder, etc... 

 

If I find out a workaround that doesn't require the explicit double precision suffix I'll post it here. Thank-you for bringing this to my attention.
回覆