Determining whether a floating point number is negative

I want to check whether a floating point number is negative in bash, but am finding the task tricky. An improvewent would be to check a condition, returning true or false.

ncondi "$n" "<" "$m"
ncondi "$n" "<" "0"

Perhaps you can search the number string for a leading dash

if [[ $n == -* ]]
then
  echo negative
else
  echo positive
fi

you could just check for a minus:

[[ $n =~ - ]]

I suppose that checking only for - would be enough if we assume the computation was performed by bc.

Am looking into using the following

"$1" =~ ^-[0-9]*([.]*[0-9]*)$

Is there a way to check truthness of numeric conditions (non only integers, but also floating point) using a bash function ?

In bash itself, you can really only test the integer parts after cutting off the fractional part. I don't use ksh, but IIRC other posts say that recent ksh versions support real numbers .

Obviously, equality of reals can be an elusive concept: it's hard enough to define "close enough to equal" in C. A further issue is dealing with unnormalised and exponent representations in data.

Edit: confirmation:

$ ksh --version
version sh (AT&T Research) 93u+ 2012-08-01
$ ksh
$ printf '%.8f\n' $(( -3.14159 / 17e-02 ))
-18.47994118
$

Okay, so now you've made me curious. Under what situation would a negative number NOT begin with a minus sign?

:thinking: :joy:

@Azhrei You are assuming the numeric format is valid. If using bc or some other decent computational program it is good, except if you get nan etc being returned.

Have adapted by regex command to

[[ "$1" =~ ^-[0-9]*([.]?[0-9]*)$ ]]

Basically, I am using [.]?. I wonder if the () around [.]?[0-9]* is required.

you can use a bit more advanced regex to handle -10.4e5 e.g. That would require (in this implementation) a look ahead not offered by bash/ksh builtins. But grep with PCRE does.

#!/bin/bash
#set -x

a='-10.43'
a='10.4e5'
a='-10.4e5'
a='5.82376'

re='^[-+]?[0-9]*[.,]?[0-9]+(?:[eE][-+]?[0-9]+)?$'

#if [[ ${a} =~ ${re} ]]; then
if [ $(grep -P "${re}" <<<"${a}") ]; then
    echo "[${a}] float"
else
    echo "[${a}] NOT float"
fi

See if it's a bit better for YOUR test cases.
You can comment out my test cases and test yours.

look here - FYI.
Here's the version based on math and bc:

#!/bin/bash
#set -x

num=-213.213332222
#num=213.213332222

if [ $(bc -l  <<< "if ( sqrt ( $num ^ 2 ) - $num == 0) 1 else 0") -eq 1 ]; then
    echo "${num} pos"
else
    echo "${num} neg"
fi

@vgersh99 I agree with your suggestion about handling more advanced floats not offered by bash/ksh builtins.

Heh. "NaN" is not a number, hence the name "not a number"! All negative numbers will have a minus sign and your initial question was whether, "a floating point number is negative". :grin:

Case "pattern matching" is builtin almost like regexp.
Here is example how to check is number and which kind of number we have.

# bash need little setup, ksh not
# check your default when using bash: shopt | grep extglob
# lazy version without testing shell
shopt -s extglob 2>/dev/null

isnumber()
{
        n=$1
        org=$n
        negative=""
        float=""
        notnumber=""
        int=""
        first=${n:0:1}
        # if 1st char is -, remove it. Maybe negative number
        [ "$first" = "-" ] && negative="negative" && n=${n:1}  # remove -,maybe negative number
        case "$n" in
               .+([0-9])) float="float" ;;
                +([0-9]).+([0-9])) float="float" ;;
                +([0-9]).) float="float" ;;
                +([0-9])) int="int" ;;
                *) negative=""
                   notnumber="notnumber"
                   ;;
        esac
        echo $org $notnumber $negative $float $int
}

#########################################
# testing
for n in 1.23 -1.23 -1.23.23 1.23.23 -1 1 a 1.23e  .32 --1.2 -1- -1.0-   -1234.  -1234.344
do
        isnumber "$n"
done

You could add the number to its absolute value and check if zero, since a negative number added to it's absolute value is zero.

Personally, I think this approach is more "mathematical" compared to matching a minus sign.

 A + (-A) = 0

There is a name for this, the "Inverse Additive Property":

Screen Shot 2021-08-23 at 8.25.28 PM

Reference:

@Neo Your suggestion, although valid, is bothersome, particularly when the addition need to be performed on floating point numbers.

I tried it and it works fine,, so your definition of "bothersome" means ???

#!/bin/bash

a=-213.213332222
b=$a*$a
z=`echo "sqrt ($b)" | bc -l`

echo "The absolute value of "$a" is "$z"."
./abs_floating_point_check.sh
The absolute value of -213.213332222 is 213.21333222200000000000.

I am almost "falling over laughing" over your use of the term "bothersome", @han. Sorry, but I've been in IT forever, and there is nothing "bothersome" about taking the ABS of floating point numbers in bash, as far as I am concerned.

It is common in math to take the square root of the square of a number to get the absolute value.

I see, because you use bc. I have a version like that.

Personally, I would never use a REGEX to do what can be easily be done with simple, basic math.

Determining if a number is negative is a math operation in my mind, not a text pattern-matching operation.

But I like math :slight_smile: and I like simple and reliable as well.

I've seen reports (e.g.financial) that enclose negative numbers in parentheses, no minus sign, or, on other occasions, numbers followed by a minus sign...