Commit c32e5ca

kravantokh <kravantokh@noreply.codeberg.org>
2026-07-18 22:34:30
Add round down. Adjust tests.
1 parent b8b9c62
Changed files (3)
testing/tests/001_safemath.c
@@ -9,7 +9,7 @@ void entry(void) {
 
 	assert( (-1^1) == -2 ); /* All safemath code assumes 2's complement. Assert it here, too. */
 
-	/* Validate correct addition and subtraction behaviour */
+	/* Validate correct basic addition and subtraction behaviour */
 	assert( S_OK( u64_add( 3, 2,       &res ))); assert( res == 5 );
 	assert( S_OK( u32_add( 3, 2, (u32*)&res ))); assert( res == 5 );
 	assert( S_OK( u16_add( 3, 2, (u16*)&res ))); assert( res == 5 );
@@ -30,19 +30,52 @@ void entry(void) {
 	assert( S_OK( s16_sub( 3, 2, (s16*)&res ))); assert( res == 1 );
 	assert( S_OK(  s8_sub( 3, 2, (s8 *)&res ))); assert( res == 1 );
 
-	/* Unsigned overflow */
+	/* Catch potential off-by-one implementation errors */
+	/* Unsigned */
+	assert( S_OK( u64_add( u64_MAX - 1, 1,       &res ))); assert(      res == u64_MAX );
+	assert( S_OK( u32_add( u32_MAX - 1, 1, (u32*)&res ))); assert( (u32)res == u32_MAX );
+	assert( S_OK( u16_add( u16_MAX - 1, 1, (u16*)&res ))); assert( (u16)res == u16_MAX );
+	assert( S_OK(  u8_add(  u8_MAX - 1, 1, (u8 *)&res ))); assert( ( u8)res ==  u8_MAX );
+
+	assert( S_OK( u64_sub( u64_MIN + 1, 1,       &res ))); assert(      res == u64_MIN ); 
+	assert( S_OK( u32_sub( u32_MIN + 1, 1, (u32*)&res ))); assert( (u32)res == u32_MIN ); 
+	assert( S_OK( u16_sub( u16_MIN + 1, 1, (u16*)&res ))); assert( (u16)res == u16_MIN ); 
+	assert( S_OK(  u8_sub(  u8_MIN + 1, 1, (u8 *)&res ))); assert( ( u8)res ==  u8_MIN );  
+
+	/* Signed */
+	assert( S_OK( s64_add( s64_MAX - 1, 1, (s64*)&res ))); assert( (s64)res == s64_MAX );
+	assert( S_OK( s32_add( s32_MAX - 1, 1, (s32*)&res ))); assert( (s32)res == s32_MAX );
+	assert( S_OK( s16_add( s16_MAX - 1, 1, (s16*)&res ))); assert( (s16)res == s16_MAX );
+	assert( S_OK(  s8_add(  s8_MAX - 1, 1, (s8 *)&res ))); assert( ( s8)res ==  s8_MAX );
+
+	assert( S_OK( s64_sub( s64_MAX - 1, -1, (s64*)&res ))); assert( (s64)res == s64_MAX );
+	assert( S_OK( s32_sub( s32_MAX - 1, -1, (s32*)&res ))); assert( (s32)res == s32_MAX );
+	assert( S_OK( s16_sub( s16_MAX - 1, -1, (s16*)&res ))); assert( (s16)res == s16_MAX );
+	assert( S_OK(  s8_sub(  s8_MAX - 1, -1, (s8 *)&res ))); assert( ( s8)res ==  s8_MAX );
+
+	assert( S_OK( s64_sub( s64_MIN + 1, 1, (s64*)&res ))); assert( (s64)res == s64_MIN ); 
+	assert( S_OK( s32_sub( s32_MIN + 1, 1, (s32*)&res ))); assert( (s32)res == s32_MIN ); 
+	assert( S_OK( s16_sub( s16_MIN + 1, 1, (s16*)&res ))); assert( (s16)res == s16_MIN ); 
+	assert( S_OK(  s8_sub(  s8_MIN + 1, 1, (s8 *)&res ))); assert( ( s8)res ==  s8_MIN );  
+
+	assert( S_OK( s64_add( s64_MIN + 1, -1, (s64*)&res ))); assert( (s64)res == s64_MIN ); 
+	assert( S_OK( s32_add( s32_MIN + 1, -1, (s32*)&res ))); assert( (s32)res == s32_MIN ); 
+	assert( S_OK( s16_add( s16_MIN + 1, -1, (s16*)&res ))); assert( (s16)res == s16_MIN ); 
+	assert( S_OK(  s8_add(  s8_MIN + 1, -1, (s8 *)&res ))); assert( ( s8)res ==  s8_MIN );  
+
+	/* Overflow */
+	/* Unsigned */
 	assert( S_FAILED( u64_add( u64_MAX, N,       &res )));
 	assert( S_FAILED( u32_add( u32_MAX, N, (u32*)&res )));
 	assert( S_FAILED( u16_add( u16_MAX, N, (u16*)&res )));
 	assert( S_FAILED(  u8_add(  u8_MAX, N, (u8 *)&res )));
 
-	/* Unsigned underflow */
-	assert( S_FAILED( u64_sub( u64_MIN, N,       &res )));
-	assert( S_FAILED( u32_sub( u32_MIN, N, (u32*)&res )));
-	assert( S_FAILED( u16_sub( u16_MIN, N, (u16*)&res )));
-	assert( S_FAILED(  u8_sub(  u8_MIN, N, (u8 *)&res )));
+	assert( S_FAILED( u64_add( u64_MAX, 1,       &res )));
+	assert( S_FAILED( u32_add( u32_MAX, 1, (u32*)&res )));
+	assert( S_FAILED( u16_add( u16_MAX, 1, (u16*)&res )));
+	assert( S_FAILED(  u8_add(  u8_MAX, 1, (u8 *)&res )));
 
-	/* Signed overflow */
+	/* Signed */
 	assert( S_FAILED( s64_add( s64_MAX,  N, (s64*)&res )));
 	assert( S_FAILED( s32_add( s32_MAX,  N, (s32*)&res )));
 	assert( S_FAILED( s16_add( s16_MAX,  N, (s16*)&res )));
@@ -52,15 +85,46 @@ void entry(void) {
 	assert( S_FAILED( s16_sub( s16_MAX, -N, (s16*)&res )));
 	assert( S_FAILED(  s8_sub(  s8_MAX, -N,  (s8 *)&res )));
 
-	/* Signed underflow */
+	assert( S_FAILED( s64_add( s64_MAX,  1, (s64*)&res )));
+	assert( S_FAILED( s32_add( s32_MAX,  1, (s32*)&res )));
+	assert( S_FAILED( s16_add( s16_MAX,  1, (s16*)&res )));
+	assert( S_FAILED(  s8_add(  s8_MAX,  1, (s8 *)&res )));
+	assert( S_FAILED( s64_sub( s64_MAX, -1, (s64*)&res )));
+	assert( S_FAILED( s32_sub( s32_MAX, -1, (s32*)&res )));
+	assert( S_FAILED( s16_sub( s16_MAX, -1, (s16*)&res )));
+	assert( S_FAILED(  s8_sub(  s8_MAX, -1,  (s8 *)&res )));
+
+	/* Underflow */
+
+	/* Unsigned */
+	assert( S_FAILED( u64_sub( u64_MIN, N,       &res )));
+	assert( S_FAILED( u32_sub( u32_MIN, N, (u32*)&res )));
+	assert( S_FAILED( u16_sub( u16_MIN, N, (u16*)&res )));
+	assert( S_FAILED(  u8_sub(  u8_MIN, N, (u8 *)&res )));
+	assert( S_FAILED( u64_sub( u64_MIN, 1,       &res )));
+	assert( S_FAILED( u32_sub( u32_MIN, 1, (u32*)&res )));
+	assert( S_FAILED( u16_sub( u16_MIN, 1, (u16*)&res )));
+	assert( S_FAILED(  u8_sub(  u8_MIN, 1, (u8 *)&res )));
+
+	/* Signed */
 	assert( S_FAILED( s64_sub( s64_MIN,  N, (s64*)&res )));
 	assert( S_FAILED( s32_sub( s32_MIN,  N, (s32*)&res )));
 	assert( S_FAILED( s16_sub( s16_MIN,  N, (s16*)&res )));
 	assert( S_FAILED(  s8_sub(  s8_MIN,  N, (s8 *)&res )));
+	assert( S_FAILED( s64_sub( s64_MIN,  1, (s64*)&res )));
+	assert( S_FAILED( s32_sub( s32_MIN,  1, (s32*)&res )));
+	assert( S_FAILED( s16_sub( s16_MIN,  1, (s16*)&res )));
+	assert( S_FAILED(  s8_sub(  s8_MIN,  1, (s8 *)&res )));
+
 	assert( S_FAILED( s64_add( s64_MIN, -N, (s64*)&res )));
 	assert( S_FAILED( s32_add( s32_MIN, -N, (s32*)&res )));
 	assert( S_FAILED( s16_add( s16_MIN, -N, (s16*)&res )));
 	assert( S_FAILED(  s8_add(  s8_MIN, -N, (s8 *)&res )));
+	assert( S_FAILED( s64_add( s64_MIN, -1, (s64*)&res )));
+	assert( S_FAILED( s32_add( s32_MIN, -1, (s32*)&res )));
+	assert( S_FAILED( s16_add( s16_MIN, -1, (s16*)&res )));
+	assert( S_FAILED(  s8_add(  s8_MIN, -1, (s8 *)&res )));
+
 
 	terminate(0);
 }
testing/tests/002_round.c
@@ -4,50 +4,26 @@
 #define N 13
 
 void entry(void) {
-
-	u64 v1 = N + 1;
-	u32 v2 = N + 1;
-	u16 v3 = N + 1;
-	u8 v4 = N + 1;
 	u64 result = N - 1;
 
-	assert( S_OK(
-		u64_round_up( v1, N, &result )
-	));
-
-	assert( S_OK(
-		u32_round_up( v2, N, (u32*)&result )
-	));
-
-	assert( S_OK(
-		u16_round_up( v3, N, (u16*)&result )
-	));
-
-	assert( S_OK(
-		u8_round_up( v4, N, (u8*)&result )
-	));
-
-	v1 = -1;
-	v2 = -1;
-	v3 = -1;
-	v4 = -1;
-
-	assert( S_FAILED(
-		u64_round_up( v1, N, &result )
-	));
-
-	assert( S_FAILED(
-		u32_round_up( v2, N, (u32*)&result )
-	));
+	/* Basic functionality test */
+	assert( S_OK( u64_round_up( N+1, N,       &result ))); assert( result == 2*N);
+	assert( S_OK( u32_round_up( N+1, N, (u32*)&result ))); assert( result == 2*N);
+	assert( S_OK( u16_round_up( N+1, N, (u16*)&result ))); assert( result == 2*N);
+	assert( S_OK(  u8_round_up( N+1, N, (u8 *)&result ))); assert( result == 2*N);
 
-	assert( S_FAILED(
-		u16_round_up( v3, N, (u16*)&result )
-	));
+	assert( S_OK( u64_round_down( 2*N-1, N,       &result ))); assert( result == N);
+	assert( S_OK( u32_round_down( 2*N-1, N, (u32*)&result ))); assert( result == N);
+	assert( S_OK( u16_round_down( 2*N-1, N, (u16*)&result ))); assert( result == N);
+	assert( S_OK(  u8_round_down( 2*N-1, N, (u8 *)&result ))); assert( result == N);
 
-	assert( S_FAILED(
-		u8_round_up( v4, N, (u8*)&result )
-	));
+	/* Overflow */
+	assert( S_FAILED( u64_round_up( u64_MAX, N,       &result )));
+	assert( S_FAILED( u32_round_up( u32_MAX, N, (u32*)&result )));
+	assert( S_FAILED( u16_round_up( u16_MAX, N, (u16*)&result )));
+	assert( S_FAILED(  u8_round_up(  u8_MAX, N, (u8 *)&result )));
 
+	/* Rounding down to 0 is valid, so no underflow test */
 
 	terminate(0);
 }
util/mathops.h
@@ -10,7 +10,7 @@
 
 #define GEN_ROUND_UP(T) \
 	static inline Status T##_round_up( T number, T multiple, T *out ) {           \
-		if( multiple == 0 ) return S_FAILURE | ERR_ARG_3 | ERR_INVALID_ARGUMENT; \
+		if( multiple == 0 ) return S_FAILURE | ERR_ARG_2 | ERR_INVALID_ARGUMENT; \
 		T rem = number%multiple;                                                 \
 		                                                                         \
 		if( rem == 0 ) {                                                         \
@@ -21,6 +21,12 @@
 		return S_SUCCESS;                                                        \
 	}
 
+#define GEN_ROUND_DOWN(T) \
+	static inline Status T##_round_down(T number, T multiple, T *out) { \
+		if( multiple == 0 ) return S_FAILURE | ERR_ARG_2 | ERR_INVALID_ARGUMENT; \
+		*out = number - (number%multiple); \
+		return S_SUCCESS; \
+	}
 
 
 GEN_ROUND_UP(u8)
@@ -28,4 +34,9 @@ GEN_ROUND_UP(u16)
 GEN_ROUND_UP(u32)
 GEN_ROUND_UP(u64)
 
+GEN_ROUND_DOWN(u8)
+GEN_ROUND_DOWN(u16)
+GEN_ROUND_DOWN(u32)
+GEN_ROUND_DOWN(u64)
+
 #endif