QB64 Phoenix Edition
Determine a value is INTEGER - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Help Me! (https://qb64phoenix.com/forum/forumdisplay.php?fid=10)
+---- Thread: Determine a value is INTEGER (/showthread.php?tid=2884)

Pages: 1 2


RE: Determine a value is INTEGER - TerryRitchie - 07-26-2024

(07-26-2024, 03:02 PM)Petr Wrote: I use these methods (versus the ones listed here):

If Value MOD 1 = 0 -> INTEGER
If Value \ 1 = Value -> INTEGER
maybe it's the same in the background, just written differently. I didn't do this using a string conversion because those conversions are slow.
Thanks for the tips. I didn't even think to investigate MOD.

(07-26-2024, 05:25 PM)Jack Wrote: Petr
I like If Value MOD 1 = 0 -> INTEGER but it remains to be tested whether it's faster or slower than using INT
integer division is rather slow especially when dealing with 64-bit integers
My guess is that it's slower given that division is at play. I'm going to incorporate it into my code and see the result.


RE: Determine a value is INTEGER - DSMan195276 - 07-26-2024

I would consider doing something like  `IF x > intval - .00001 AND x < intval + .00001 THEN` to check for an integer value, that allows for the fact that it may not always be an exact integer but is still close enough for your approximation to work. The other suggestions on this page could work for detecting if it's an exact integer value, but that's probably not what you want to do since that's the likely cause of the "hiccups" you mentioned where the value isn't actually an integer even though it "should" be.


RE: Determine a value is INTEGER - Jack - 07-26-2024

@Petr
your expression If Value MOD 1 = 0 -> INTEGER looks suspicious, in order for MOD to work it must first convert Value to Integer, I suggest that you run some tests to make sure that it's doing what you expect, I suspect that it's not


RE: Determine a value is INTEGER - Kernelpanic - 07-26-2024

@DSMan195276 - one should be able to integrate C/C++ functions using Declare . . . Can't one integrate the floor() function to determine an integer? Is that possible?

I don't have the nerve to try this out anymore. But it should work . . .
The example in C:

Code: (Select All)

//Ganzzahlwert einer eingegebenen Zahl ermitteln - 27. Juli 2024

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main (void)
{
  float wert1, wert2, wert3, wert4;

printf("\nDen Ganzzahlwert einer eingegebenen Zahl ermitteln");
printf("\n==================================================");

printf("\n\nGegebene Werte: 1.6 - 1.2 - 2.8 - 2.3\n\n");

  wert1 = 1.6;
  wert2 = 1.2;
  wert3 = 2.8;
  wert4 = 2.3;

  printf("Wert-1 = %.1f\n", floor(wert1));
  printf("Wert-2 = %.1f\n", floor(wert2));
  printf("Wert-3 = %.1f\n", floor(wert3));
  printf("Wert-4 = %.1f\n", floor(wert4));
 
  return(0);
}



RE: Determine a value is INTEGER - DSMan195276 - 07-27-2024

@KernelPanic that's possible to do with `Declare Library` but the `INT` command is actually just `std::floor` already.


RE: Determine a value is INTEGER - SMcNeill - 07-27-2024

By Golly, my over-engineered NumType will tell you if it's an integer!  Exactly which types of integer it might be as well!  Big Grin

Code: (Select All)
CONST limit = 16

DIM test(limit) AS STRING

DATA "123a.3","-123.456","--234","1.23E15","123","dogfood","678.965","54678","-987134","1E15"
DATA "&HFF","&B1001111","&O17","&HFF&&","&B12000222","1.E-12"

FOR i = 1 TO limit
    READ test(i)
NEXT


FOR i = 1 TO limit
    PRINT "TEST #"; i; ": "; test(i) + " "
    result = NumType(test(i))
    IF result = 0 THEN PRINT "INVALID: "; NumErr$
    IF result AND 1 THEN PRINT "Valid Unsigned Bit.  ";
    IF result AND 2 THEN PRINT "Valid Unsigned Byte.  ";
    IF result AND 4 THEN PRINT "Valid Unsigned Integer.  ";
    IF result AND 8 THEN PRINT "Valid Unsigned Long.  ";
    IF result AND 16 THEN PRINT "Valid Unsigned Integer64.  ";
    IF result AND 32 THEN PRINT "Valid Unsigned Bit.  ";
    IF result AND 64 THEN PRINT "Valid Signed Byte.  ";
    IF result AND 128 THEN PRINT "Valid Signed Integer.  ";
    IF result AND 256 THEN PRINT "Valid Signed Long.  ";
    IF result AND 512 THEN PRINT "Valid Signed Integer64.  ";
    IF result AND 1024 THEN PRINT "Valid Single.  ";
    IF result AND 2048 THEN PRINT "Valid Double.  ";
    IF result AND 4096 THEN PRINT "Valid Float.  ";
    IF result AND 8192 THEN PRINT "Valid Unsigned Offset.  ";
    IF result AND 16384 THEN PRINT "Valid Signed Offset.  ";
    PRINT
    PRINT
    SLEEP
NEXT

FUNCTION NumType~% (text$)
    SHARED NumErr$
    DIM TempNum AS INTEGER
    temp$ = UCASE$(_TRIM$(text$))
    NumErr$ = "": TempNum = 0

    'First look for manually assigned types
    r1$ = RIGHT$(temp$, 1): r = 1
    r2$ = LEFT$(RIGHT$(temp$, 2), 1)
    SELECT CASE r1$
        CASE "`"
            TestFor = 1 'bit
        CASE "%"
            IF r2$ = "%" THEN
                r = 2
                TestFor = 2 'byte
            ELSE
                TestFor = 3 'integer
            END IF
        CASE "&" 'long, int64, offset
            IF r2$ = "&" THEN
                r = 2
                TestFor = 5 'int64
            ELSEIF r2$ = "%" THEN
                r = 2
                TestFor = 9 'offset
            ELSE
                TestFor = 4 'long
            END IF
        CASE "!" 'single
            TestFor = 6
        CASE "#" 'double, float
            IF r2$ = "#" THEN
                r = 2
                TestFor = 8 'float
            ELSE
                TestFor = 7 'double
            END IF
        CASE ELSE 'there's no set type
            TestFor = 0
            r = 0
    END SELECT


    temp$ = LEFT$(temp$, LEN(temp$) - r) 'strip off the type symbol
    SELECT CASE TestFor
        CASE 1 TO 5, 9
            r$ = RIGHT$(temp$, 1)
            IF r$ = "~" THEN Unsigned = -1: temp$ = LEFT$(temp$, LEN(temp$) - 1)
    END SELECT

    'check for valid prefixes

    l$ = LEFT$(temp$, 2)
    SELECT CASE l$
        CASE "&H"
            temp$ = MID$(temp$, 3)
            FOR i = 1 TO LEN(temp$)
                t$ = MID$(temp$, i, 1)
                SELECT CASE t$
                    CASE "0" TO "9", "A" TO "F" 'valid
                    CASE ELSE
                        NumErr$ = NumErr$ + "Invalid Character (" + t$ + ") encountered.  "
                END SELECT
            NEXT
            IF NumErr$ <> "" THEN EXIT FUNCTION
            GOTO evaluateintegers
        CASE "&B"
            temp$ = MID$(temp$, 3)
            FOR i = 1 TO LEN(temp$)
                t$ = MID$(temp$, i, 1)
                SELECT CASE t$
                    CASE "0", "1" 'only valid bit characters
                    CASE ELSE
                        NumErr$ = NumErr$ + "Invalid Character (" + t$ + ") encountered.  "
                END SELECT
            NEXT
            IF NumErr$ <> "" THEN EXIT FUNCTION
            GOTO evaluateintegers
        CASE "&O"
            temp$ = MID$(temp$, 3)
            FOR i = 1 TO LEN(temp$)
                t$ = MID$(temp$, i, 1)
                SELECT CASE t$
                    CASE "0" TO "7" 'only valid oct characters
                    CASE ELSE
                        NumErr$ = NumErr$ + "Invalid Character (" + t$ + ") encountered.  "
                END SELECT
            NEXT
            IF NumErr$ <> "" THEN EXIT FUNCTION
            GOTO evaluateintegers
    END SELECT


    'Test for easy integers
    'First check for positive/negative values; flag for invalid cases of multiple negation.
    IF MID$(temp$, 1, 1) = "-" THEN
        negative = -1: temp$ = MID$(temp$, 2) 'strip off the initial negative
    ELSEIF MID$(temp$, 1, 1) = "+" THEN
        temp$ = MID$(temp$, 2) 'strip off the initial positive
    END IF

    FOR i = 1 TO LEN(temp$)
        IF MID$(temp$, i, 1) = "-" THEN minus = minus + 1
        IF MID$(temp$, i, 1) = "+" THEN plus = plus + 1
        IF MID$(temp$, i, 1) = "." THEN period = period + 1 'Go ahead and check for multiple periods while we're at it.
        IF MID$(temp$, i, 1) = "E" OR MID$(temp$, i, 1) = "D" THEN
            Exponent = Exponent + 1
            IF MID$(temp$, i + 1, 1) = "-" OR MID$(temp$, i + 1, 1) = "+1" THEN ExponentSign = -1
        END IF
    NEXT

    IF period = 0 AND Exponent = 0 THEN 'we should only have integers to process
        FOR i = 1 TO LEN(temp$)
            t$ = MID$(temp$, i, 1)
            IF t$ < "0" OR t$ > "9" THEN NumErr$ = NumErr$ + "Invalid Character (" + t$ + ") encountered.  ": EXIT FUNCTION
        NEXT
        GOTO evaluateintegers
    END IF

    'At this point forward, we should only have REAL numbers to process

    IF Exponent > 1 THEN NumErr$ = NumErr$ + "Multiple E/D exponent characters in string.  ": EXIT FUNCTION

    IF ExponentSign = 0 THEN
        IF minus THEN NumErr$ = NumErr$ + "Multiple negative signs (-) encountered.  ": EXIT FUNCTION
        IF plus THEN NumErr$ = NumErr$ + "Multiple negative signs (-) encountered.  ": EXIT FUNCTION
    ELSE
        IF minus > 1 THEN NumErr$ = NumErr$ + "Multiple negative signs (-) encountered.  ": EXIT FUNCTION
        IF plus > 1 THEN NumErr$ = NumErr$ + "Multiple negative signs (-) encountered.  ": EXIT FUNCTION
    END IF

    IF period > 1 THEN NumErr$ = NumErr$ + "Multiple decimal points (.) encountered.  ": EXIT FUNCTION

    IF Exponent AND period THEN
        e = INSTR(temp$, "E")
        IF e = 0 THEN e = INSTR(temp$, "D")
        p = INSTR(temp$, ".")
        IF p > e THEN NumErr$ = NumErr$ + "Decimal points (.) AFTER E/D exponent encountered.  ": EXIT FUNCTION
    END IF


    FOR i = 1 TO LEN(temp$)
        t$ = MID$(temp$, i, 1)
        SELECT CASE t$
            CASE "0" TO "9", "-", "+", ".", "D", "E" 'we should have validated all these characters earlier
            CASE ELSE 'so anything else is invalid
                NumErr$ = NumErr$ + "Invalid Character (" + t$ + ") encountered.  ": EXIT FUNCTION
        END SELECT
    NEXT

    IF NumErr$ <> "" THEN EXIT FUNCTION


    'We should've passed all the error checking by this point -- I think...


    evaluateintegers:
    t## = VAL(text$)

    'first compare for all types
    IF INT(t##) = t## THEN
        IF t## = -1 OR t## = 0 THEN TempNum = TempNum OR 32 'signed bit
        IF t## >= -128 AND t## <= 127 THEN TempNum = TempNum OR 64 'signed byte
        IF t## >= -32768 AND t## <= 32767 THEN TempNum = TempNum OR 128 'signed integer
        IF t## >= -2147483648 AND t## <= 2147483647 THEN TempNum = TempNum OR 256 'signed long
        IF t## >= -9223372036854775808 AND t## <= 9223372036854775807 THEN
            TempNum = TempNum OR 512 'signed integer64
            TempNum = TempNum OR 16384 'signed offset
        END IF
        IF t## = 1 OR t## = 0 THEN TempNum = TempNum OR 1 'unsigned bit
        IF t## >= 0 AND t## <= 255 THEN TempNum = TempNum OR 2 'unsigned byte
        IF t## >= 0 AND t## <= 65535 THEN TempNum = TempNum OR 4 'unsigned integer
        IF t## >= 0 AND t## <= 4294967295 THEN TempNum = TempNum OR 8 'unsigned long
        IF t## >= 0 AND t## <= 18446744073709551615 THEN
            TempNum = TempNum OR 16 'unsigned integer64
            TempNum = TempNum OR 8192 'unsigned offset
        END IF
    END IF

    IF t## >= -2.802597D45 AND t## <= 3.402823D+38 THEN
        TempNum = TempNum OR 1024 'single
    END IF
    IF t## >= -4.490656458412465E324 AND t## <= 1.797693134862310E+308 THEN TempNum = TempNum OR 2048 'double
    IF t## >= -1.18E4932 AND t## <= 1.18E+4932 THEN TempNum = TempNum OR 4096 'float

    IF r THEN 'we have specific suffix; only decide if the value is valid for it
        TempNum = 0
        IF NOT Unsigned THEN 'unsigned
            SELECT CASE TestFor
                CASE 1
                    IF t## = -1 OR t## = 0 THEN TempNum = 32 'signed bit
                CASE 2
                    IF t## >= -128 AND t## <= 127 THEN TempNum = 64 'signed byte
                CASE 3
                    IF t## >= -32768 AND t## <= 32767 THEN TempNum = 128 'signed integer
                CASE 4
                    IF t## >= -2147483648 AND t## <= 2147483647 THEN TempNum = 256 'signed long
                CASE 5, 9
                    IF t## >= -9223372036854775808 AND t## <= 9223372036854775807 THEN
                        IF TestFor = 5 THEN
                            TempNum = 512 'signed integer64
                        ELSE
                            TempNum = 16384 'signed offset
                        END IF
                    END IF
                CASE 6
                    IF t## >= -2.802597E-45 AND t## <= 3.402823E+38 THEN TempNum = 1024 'single
                CASE 7
                    IF t## >= -4.490656458412465E-324 AND t## <= 1.797693134862310E+308 THEN TempNum = 2048 'double
                CASE 9
                    IF t## >= -1.18E-4932 AND t## <= 1.18E+4932 THEN TempNum = 4096 'float
            END SELECT
        ELSE
            SELECT CASE TestFor
                CASE 1
                    IF t## = 0 OR t## = 1 THEN TempNum = 1 'unsigned bit
                CASE 2
                    IF t## >= 0 AND t## <= 255 THEN TempNum = 2 'unsigned byte
                CASE 3
                    IF t## >= 0 AND t## <= 65535 THEN TempNum = 4 'unsigned integer
                CASE 4
                    IF t## >= 0 AND t## <= 4294967295 THEN TempNum = 8 'unsigned long
                CASE 5, 9
                    IF t## >= 0 AND t## <= 18446744073709551615 THEN
                        IF TestFor = 5 THEN
                            TempNum = 16 'unsigned integer64
                        ELSE
                            TempNum = 8192 'unsigned offset
                        END IF
                    END IF
            END SELECT
        END IF
        IF TempNum = 0 THEN NumErr$ = "Invalid Suffix.  "
    END IF
    NumType = TempNum
END FUNCTION



RE: Determine a value is INTEGER - bplus - 07-27-2024

There you go @TerryRitchie Steve has an "amazing" @CR solution, just use that! LOL

Welcome back @SMcNeill I wonder if you have a story to tell from your absence?


RE: Determine a value is INTEGER - Kernelpanic - 07-27-2024

(07-27-2024, 04:30 AM)DSMan195276 Wrote: @KernelPanic that's possible to do with `Declare Library` but the `INT` command is actually just `std::floor` already.
That's right, one can do that with Basic too! That's why it seemed a bit strange to me, I hadn't thought of INT.  Sad

Code: (Select All)

'Gleitkommazahl in Ganzzahligen- und Nachkommateil aufteilen - 27. Juli 2024

Option _Explicit

Dim As Double eingabe, vorKomma, nachKomma

Print
Input "Fliesskommazahl eingeben: ", eingabe

'Nachkommarest ermitteln
nachKomma = Abs(eingabe - Fix(eingabe))

'Vorkommateil, ganzzahliger Teil
vorKomma = eingabe - nachKomma

Print
Print Using "Ganzzahliger Teil: ####"; vorKomma
Print
Print Using "Nachkommateil    : .###"; nachKomma

Print: Print
Print Using "Ganzzahliger Teil mit INT: ####"; Int(eingabe)

End