I try to implement the C sqrt function in Nasm elf64, it works correctly without argument (with the value of "a" define in the function), but when I try to pass an argument to this function ,the code return an error "Stopped reason: SIGFPE".
Here's my code
The c function
int le_sqrt(int a) {
int n=1;
int number = a;
for (int i=0; i<10; i++) {
n=(n+number/n)/2;
}
return n;
}
The nasm program
bits 64
global _start
extern le_sqrt
_start:
mov rbp,9 ;argument
push rbp ;same argument push
call le_sqrt ; my c function
mov rax,60 ;exit program
mov rsi,0
syscall
If you want to call le_sqrt(9) with System V AMD64 ABI calling convention, do this:
_start:
mov rdi,9
call le_sqrt
SIGFPE usually happens when you divide a number by 0. In your assembly program, you are using mov rbp, 9 for passing an argument to c function, which might be wrong in your case. It becomes obvious since you're getting SIGFPE. See Microsoft calling conventions and System V ABI calling conventions (for 64-bit). For 32-bit, follow these calling conventions.
In a C program, there is a swap function and this function takes a parameter called x.I expect it to return it by changing the x value in the swap function inside the main function.
When I value the parameter as a variable, I want it, but when I set an integer value directly for the parameter, the program produces random outputs.
#include <stdio.h>
int swap (int x) {
x = 20;
}
int main(void){
int y = 100;
int a = swap(y);
printf ("Value: %d", a);
return 0;
}
Output of this code: 100 (As I wanted)
But this code:
#include <stdio.h>
int swap (int x) {
x = 20;
}
int main(void){
int a = swap(100);
printf ("Value: %d", a);
return 0;
}
Return randomly values such as Value: 779964766 or Value:1727975774.
Actually, in two codes, I give an integer type value into the function, even the same values, but why are the outputs different?
First of all, C functions are call-by-value: the int x arg in the function is a copy. Modifying it doesn't modify the caller's copy of whatever they passed, so your swap makes zero sense.
Second, you're using the return value of the function, but you don't have a return statement. In C (unlike C++), it's not undefined behaviour for execution to fall off the end of a non-void function (for historical reasons, before void existed, and function returns types defaulted to int). But it is still undefined behaviour for the caller to use a return value when the function didn't return one.
In this case, returning 100 was the effect of the undefined behaviour (of using the return value of a function where execution falls off the end without a return statement). This is a coincidence of how GCC compiles in debug mode (-O0):
GCC -O0 likes to evaluate non-constant expressions in the return-value register, e.g. EAX/RAX on x86-64. (This is actually true for GCC across architectures, not just x86-64). This actually gets abused on codegolf.SE answers; apparently some people would rather golf in gcc -O0 as a language than ANSI C. See this "C golfing tips" answer and the comments on it, and this SO Q&A about why i=j inside a function putting a value in RAX. Note that it only works when GCC has to load a value into registers, not just do a memory-destination increment like add dword ptr [rbp-4], 1 for x++ or whatever.
In your case (with your code compiled by GCC10.2 on the Godbolt compiler explorer)
int y=100; stores 100 directly to stack memory (the way GCC compiles your code).
int a = swap(y); loads y into EAX (for no apparent reason), then copies to EDI to pass as an arg to swap. Since GCC's asm for swap doesn't touch EAX, after the call, EAX=y, so effectively the function returns y.
But if you call it with swap(100), GCC doesn't end up putting 100 into EAX while setting up the args.
The way GCC compiles your swap, the asm doesn't touch EAX, so whatever main left there is treated as the return value.
main:
...
mov DWORD PTR [rbp-4], 100 # y=100
mov eax, DWORD PTR [rbp-4] # load y into EAX
mov edi, eax # copy it to EDI (first arg-passing reg)
call swap # swap(y)
mov DWORD PTR [rbp-8], eax # a = EAX as the retval = y
...
But with your other main:
main:
... # nothing that touches EAX
mov edi, 100
call swap
mov DWORD PTR [rbp-4], eax # a = whatever garbage was there on entry to main
...
(The later ... reloads a as an arg for printf, matching the ISO C semantics because GCC -O0 compiles each C statement to a separate block of asm; thus the later ones aren't affected by the earlier UB (unlike in the general case with optimization enabled), so do just print whatever's in a's memory location.)
The swap function compiles like this (again, GCC10.2 -O0):
swap:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-4], 20
nop
pop rbp
ret
Keep in mind none of this has anything to do with valid portable C. This (using garbage left in memory or registers) one of the kinds of things you see in practice from C that invokes undefined behaviour, but certainly not the only thing. See also What Every C Programmer Should Know About Undefined Behavior from the LLVM blog.
This answer is just answering the literal question of what exactly happened in asm. (I'm assuming un-optimized GCC because that easily explains the result, and x86-64 because that's a common ISA, especially when people forget to mention any ISA.)
Other compilers are different, and GCC will be different if you enable optimization.
You need to use return or use pointer.
Using return function.
#include <stdio.h>
int swap () {
return 20;
}
int main(void){
int a = swap(100);
printf ("Value: %d", a);
return 0;
}
Using pointer function.
#include <stdio.h>
int swap (int* x) {
(*x) = 20;
}
int main(void){
int a;
swap(&a);
printf ("Value: %d", a);
return 0;
}
I have seen various answer here that depicts Strange behavior of pow function in C.
But I Have something different to ask here.
In the below code I have initialized int x = pow(10,2) and int y = pow(10,n) (int n = 2).
In first case it when I print the result it shows 100 and in the other case it comes out to be 99.
I know that pow returns double and it gets truncated on storing in int, but I want to ask why the output comes to be different.
CODE1
#include<stdio.h>
#include<math.h>
int main()
{
int n = 2;
int x;
int y;
x = pow(10,2); //Printing Gives Output 100
y = pow(10,n); //Printing Gives Output 99
printf("%d %d" , x , y);
}
Output : 100 99
Why is the output coming out to be different. ?
My gcc version is 4.9.2
Update :
Code 2
int main()
{
int n = 2;
int x;
int y;
x = pow(10,2); //Printing Gives Output 100
y = pow(10,n); //Printing Gives Output 99
double k = pow(10,2);
double l = pow(10,n);
printf("%d %d\n" , x , y);
printf("%f %f\n" , k , l);
}
Output : 100 99
100.000000 100.000000
Update 2 Assembly Instructions FOR CODE1
Generated Assembly Instructions GCC 4.9.2 using gcc -S -masm=intel :
.LC1:
.ascii "%d %d\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
push ebp
mov ebp, esp
and esp, -16
sub esp, 48
call ___main
mov DWORD PTR [esp+44], 2
mov DWORD PTR [esp+40], 100 //Concerned Line
fild DWORD PTR [esp+44]
fstp QWORD PTR [esp+8]
fld QWORD PTR LC0
fstp QWORD PTR [esp]
call _pow //Concerned Line
fnstcw WORD PTR [esp+30]
movzx eax, WORD PTR [esp+30]
mov ah, 12
mov WORD PTR [esp+28], ax
fldcw WORD PTR [esp+28]
fistp DWORD PTR [esp+36]
fldcw WORD PTR [esp+30]
mov eax, DWORD PTR [esp+36]
mov DWORD PTR [esp+8], eax
mov eax, DWORD PTR [esp+40]
mov DWORD PTR [esp+4], eax
mov DWORD PTR [esp], OFFSET FLAT:LC1
call _printf
leave
ret
.section .rdata,"dr"
.align 8
LC0:
.long 0
.long 1076101120
.ident "GCC: (tdm-1) 4.9.2"
.def _pow; .scl 2; .type 32; .endef
.def _printf; .scl 2; .type 32; .endef
I know that pow returns double and it gets truncated on storing in int, but I want to ask why the output comes to be different.
You must first, if you haven't already, divest yourself of the idea that floating-point numbers are in any way sensible or predictable. double only approximates real numbers and almost anything you do with a double is likely to be an approximation to the actual result.
That said, as you have realized, pow(10, n) resulted in a value like 99.99999999999997, which is an approximation accurate to 15 significant figures. And then you told it to truncate to the largest integer less than that, so it threw away most of those.
(Aside: there is rarely a good reason to convert a double to an int. Usually you should either format it for display with something like sprintf("%.0f", x), which does rounding correctly, or use the floor function, which can handle floating-point numbers that may be out of the range of an int. If neither of those suit your purpose, like in currency or date calculations, possibly you should not be using floating point numbers at all.)
There are two weird things going on here. First, why is pow(10, n) inaccurate? 10, 2, and 100 are all precisely representable as double. The best answer I can offer is that the C standard library you are using has a bug. (The compiler and the standard library, which I assume are gcc and glibc, are developed on different release schedules and by different teams. If pow is returning inaccurate results, that is probably a bug in glibc, not gcc.)
In the comments on your question, amdn found a glibc bug to do with FP rounding that might be related and another Q&A that goes into more detail about why this happens and how it's not a violation of the C standard. chux's answer also addresses this. (C doesn't require implementation of IEEE 754, but even if it did, pow isn't required to use correct rounding.) I will still call this a glibc bug, because it's an undesirable property.
(It's also conceivable, though unlikely, that your processor's FPU is wrong.)
Second, why is pow(10, n) different from pow(10, 2)? This one is far easier. gcc optimizes away function calls for which the result can be calculated at compile time, so pow(10, 2) is almost certainly being optimized to 100.0. If you look at the generated assembly code, you will find only one call to pow.
The GCC manual, section 6.59 describes which standard library functions may be treated in this way (follow the link for the full list):
The remaining functions are provided for optimization purposes.
With the exception of built-ins that have library equivalents such as the standard C library functions discussed below, or that expand to library calls, GCC built-in functions are always expanded inline and thus do not have corresponding entry points and their address cannot be obtained. Attempting to use them in an expression other than a function call results in a compile-time error.
[...]
The ISO C90 functions abort, abs, acos, asin, atan2, atan, calloc, ceil, cosh, cos, exit, exp, fabs, floor, fmod, fprintf, fputs, frexp, fscanf, isalnum, isalpha, iscntrl, isdigit, isgraph, islower, isprint, ispunct, isspace, isupper, isxdigit, tolower, toupper, labs, ldexp, log10, log, malloc, memchr, memcmp, memcpy, memset, modf, pow, printf, putchar, puts, scanf, sinh, sin, snprintf, sprintf, sqrt, sscanf, strcat, strchr, strcmp, strcpy, strcspn, strlen, strncat, strncmp, strncpy, strpbrk, strrchr, strspn, strstr, tanh, tan, vfprintf, vprintf and vsprintf are all recognized as built-in functions unless -fno-builtin is specified (or -fno-builtin-function is specified for an individual function).
So it would seem you can disable this behavior with -fno-builtin-pow.
Why is the output coming out to be different. ? (in the updated appended code)
We do not know the values are that different.
When comparing the textual out of int/double, be sure to print the double with sufficient precision to see if it is 100.000000 or just near 100.000000 or in hex to remove all doubt.
printf("%d %d\n" , x , y);
// printf("%f %f\n" , k , l);
// Is it the FP number just less than 100?
printf("%.17e %.17e\n" , k , l); // maybe 9.99999999999999858e+01
printf("%a %a\n" , k , l); // maybe 0x1.8ffffffffffff0000p+6
Why is the output coming out to be different. ? (in the original code)
C does not specify the accuracy of most <math.h> functions. The following are all compliant results.
// Higher quality functions return 100.0
pow(10,2) --> 100.0
// Lower quality and/or faster one may return nearby results
pow(10,2) --> 100.0000000000000142...
pow(10,2) --> 99.9999999999999857...
Assigning a floating point (FP) number to an int simple drops the fraction regardless of how close the fraction is to 1.0
When converting FP to an integer, better to control the conversion and round to cope with minor computational differences.
// long int lround(double x);
long i = lround(pow(10.0,2.0));
You're not the first to find this. Here's a discussion form 2013:
pow() cast to integer, unexpected result
I'm speculating that the assembly code produced by the tcc guys is causing the second value to be rounded down after calculating a result that is REALLY close to 100.
Like mikijov said in that historic post, looks like the bug has been fixed.
As others have mentioned, Code 2 returns 99 due to floating point truncation. The reason why Code 1 returns a different and correct answer is because of a libc optimization.
When the power is a small positive integer, it is more efficient to perform the operation as repeated multiplication. The simpler path removes roundoff. Since this is inlined you don't see function calls being made.
You've fooled it into thinking that the inputs are real and so it gives an approximate answer, which happens to be slightly under 100, e.g. 99.999999 that is then truncated to 99.
Hello I am trying to learn assembly and learn how to work with floating point numbers in x86_64. From what I understand arguments are passed in xmm0, xmm1, xmm2, and so on, and the result is returned in xmm0. So I am trying to make a simple assembly function that adds to double together. Here is the function
.text
.global floatadd
.type floatadd,#function
floatadd:
addsd %xmm1,%xmm0
ret
And here is the C code I am using as well.
#include<stdio.h>
int main(){
double a = 1.5;
double b = 1.2;
double c = floatadd(a,b);
printf("result = %f\n",c);
}
I have been trying to following what is happening in gdb. When I set a breakpoint in my function I can see xmm0 has 1.5 and xmm1 has 1.2 and when they are added together they 2.7. In gdb print $xmm0 gives v2_double = {2.7000000000000002, 0} However when my function returns from main and calls
cvtsi2sd %eax,%xmm0
Print $xmm0 becomes v2_double = {2, 0}. I am not sure why gcc calls that or why it is uses the 32bit register instead of the 64bit register. I have tried using the modifier %lf, and %f and both of them do the same thing.
What is happening?
The problem is that you failed to declare floatadd before calling it. So the compiler assumes it returns an int in %eax and converts that int to a double. Add the declaration:
double floatadd(double, double);
before main.
Using -Wall or whatever equivalent your compiler uses to enable warnings would probably have told you about this problem...