Reputation: 20124
I have some array,
float *large_float_array;
that I calloc and attempt to memset after a loop
large_float_array = (float*)calloc(NUM, sizeof(float));
// .. do stuff with float array
memset(large_float_array, 0, sizeof(large_float_array);
// .. do other stuff
but it seems large_float_array
is not actually zero'd out. Why is this? Is this due to the special properties of floating point numbers? If so, I'm wondering how I could fix this.
P.S. does calloc
actually work here too?
Upvotes: 1
Views: 22744
Reputation: 145297
Since large_float_array
is a pointer, you cannot use sizeof(large_float_array)
to specify the number of bytes to clear: sizeof(large_float_array)
is just the size of the pointer, typically 4 or 8 bytes. To clear the whole array, you must write:
memset(large_float_array, 0, NUM * sizeof(float));
or
memset(large_float_array, 0, NUM * sizeof(*large_float_array));
Note that memset
sets the memory to all bits zero. The C standard does not guarantee that all bits zero be a representation of 0.0
, But in all current architectures it is the case, so it will work as expected.
The strictly conforming way to clear your array is this:
#include <stdlib.h>
...
for (size_t i = 0; i < NUM; i++) {
large_float_array[i] = 0;
}
If the target systems represents 0.0
as all bits zero, modern compilers will optimize the above loop as a call to memset
as can be verified with Goldbolt's Compiler Explorer.
PS: under the same assumption, calloc()
would be a better choice: the block returned is initialized to all bits zero and for large sparse matrices, allocating via calloc()
may actually be more cache efficient on many systems.
Upvotes: 12
Reputation: 1167
You can't use memset because it accepts int
as value type, but float may be stored in different way, so you should use a regular cycle
for (size_t i = 0; i < NUM; ++i) {
large_float_array[i] = 0.;
}
Upvotes: 6
Reputation: 154228
Post has multiple issues.
Following does not have balanced ()
. Unclear what OP's true code may be.
memset(large_float_array, 0, sizeof(large_float_array);
Assuming the above simple used another )
, then code was only zero filling a few bytes with zero as sizeof(large_float_array)
is the size of a pointer.
memset(large_float_array, 0, sizeof(large_float_array));
OP likely wanted something like which would zero fill the allocated space with zero bits. This will bring the allocated memory into the same state as calloc()
.
memset(large_float_array, 0, NUM *sizeof *large_float_array);
Code should use the following. A good compiler will optimize this trivial loop into fast code.
for (size_t i=0; i<NUM; i++) {
large_float_array[i] = 0.0f;
}
A way to use the fast memcpy()
is to copy the float
into the first element of the array, then the first element to the 2nd element, then the first 2 elements to elements 3 & 4, then the first 4 elements to elements 4,5,6,7 ...
void *memset_size(void *dest, size_t n, const char *src, size_t element) {
if (n > 0 && element > 0) {
memcpy(dest, src, element); // Copy first element
n *= element;
while (n > element) {
size_t remaining = n - element;
size_t n_this_time = remaining > element ? element : remaining;
memcpy((char*)dest + element, dest, n_this_time);
element += n_this_time;
}
}
return dest;
}
float pi = M_PI;
float a[10000];
memset_size(a, 10000, &pi, sizeof pi);
As with all optimizations, consider clearly written code first and then employ candidates such as the above in the select cases that warrant it.
Upvotes: 1
Reputation: 37954
The effect of intializing memory area by calloc
and using memset
function with 0
value is actually the same. In other words, calloc
is no more "type-wise" than memset
. They both just set memory with all bits zero*.
C11 (N1570) 7.22.3.2/2 The calloc function:
The
calloc
function allocates space for an array of nmemb objects, each of whose size is size. The space is initialized to all bits zero.296)
Footnote 296 (informative only):
Note that this need not be the same as the representation of floating-point zero or a null pointer constant.
*) It's conceivable that calloc
could return address of memory location that is already pre-initialized with zeros, thus it may be faster than malloc + memset
combo.
Upvotes: 3