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?連結已複製
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ômeHi 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 infCheck 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.
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.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.--- 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
--- 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.
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.