Skip to content

Commit 4a246be

Browse files
committed
Merge pull request #55 from datastax/53
53 - Fix: Decimal/Varint incorrectly encoding negative values and 0
2 parents e323f05 + 29ebb81 commit 4a246be

File tree

1 file changed

+53
-13
lines changed

1 file changed

+53
-13
lines changed

ext/util/math.c

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -459,25 +459,65 @@ import_twos_complement(cass_byte_t* data, size_t size, mpz_t* number)
459459

460460
/* negative value */
461461
if ((data[0] & 0x80) == 0x80) {
462-
/* invert bits */
463-
mpz_com(*number, *number);
464-
/* add one */
465-
mpz_add_ui(*number, *number, 1);
466-
/* negate the value */
467-
mpz_neg(*number, *number);
462+
/* mpz_import() imports the two's complement value as an unsigned integer
463+
* so this needs to subtract 2^(8 * num_bytes) to get the negative value.
464+
*/
465+
mpz_t temp;
466+
mpz_init(temp);
467+
mpz_set_ui(temp, 1);
468+
mpz_mul_2exp(temp, temp, 8 * size);
469+
mpz_sub(*number, *number, temp);
470+
mpz_clear(temp);
468471
}
469472
}
470473

471474
cass_byte_t*
472475
export_twos_complement(mpz_t number, size_t* size)
473476
{
474-
/* negative, do two's complement */
475-
if (mpz_sgn(number) == -1) {
476-
/* invert bits */
477-
mpz_com(number, number);
478-
/* add one */
479-
mpz_add_ui(number, number, 1);
477+
cass_byte_t* bytes;
478+
479+
if (mpz_sgn(number) == 0) {
480+
/* mpz_export() returns NULL for 0 */
481+
bytes = (cass_byte_t*) malloc(sizeof(cass_byte_t));
482+
*bytes = 0;
483+
*size = 1;
484+
} else if (mpz_sgn(number) == -1) {
485+
/* mpz_export() ignores sign and only exports abs(number)
486+
* so this needs to convert the number to the two's complement
487+
* unsigned value.
488+
*/
489+
size_t n;
490+
mpz_t temp;
491+
492+
/* determine the number of bytes used in the two's complement
493+
* respresentation.
494+
*/
495+
n = mpz_sizeinbase(number, 2) / 8 + 1;
496+
497+
/* there's a special case for -2^(8 * n) numbers e.g. -128 (1000 0000) and
498+
* -32768 (100 0000 0000 0000), etc. that can be handled by n - 1 bytes in
499+
* two's complement.
500+
*/
501+
if (mpz_scan1(number, 0) == (8 * (n - 1)) - 1) {
502+
n--;
503+
}
504+
505+
/* Add 2^(8 * num_bytes) to get the unsigned value e.g.
506+
* -1 + 2^8 = 255
507+
* -128 + 2^8 = 128
508+
* -129 + 2^16 = 65407
509+
* -32768 + 2^16 = 32768
510+
* ...
511+
*/
512+
mpz_init(temp);
513+
mpz_set_ui(temp, 1);
514+
mpz_mul_2exp(temp, temp, 8 * n);
515+
mpz_add(temp, number, temp);
516+
bytes = (cass_byte_t*) mpz_export(NULL, size, 1, sizeof(cass_byte_t), 1, 0, temp);
517+
mpz_clear(temp);
518+
} else {
519+
bytes = (cass_byte_t*) mpz_export(NULL, size, 1, sizeof(cass_byte_t), 1, 0, number);
480520
}
481521

482-
return (cass_byte_t*) mpz_export(NULL, size, 1, sizeof(cass_byte_t), 1, 0, number);
522+
return bytes;
483523
}

0 commit comments

Comments
 (0)