Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Absolute value of a two's complement number

2,970 views
Skip to first unread message

Symon

unread,
Apr 22, 2010, 6:27:27 AM4/22/10
to
On 4/22/2010 3:26 AM, dlopez wrote:
> Hi,
> I'm trying to calculate the absolute value of a signed number (two's
> complement).
>
> Right now, I sign extend the input, and when msb=1, inverse all bits and
> add 1. The sign extend is to take care of the most negative number.
>

So, if I read numeric_std correctly (see below), in VHDL, for (say) four
bit signed numbers, abs("1000"), aka abs(-8), equals zero. What am I
missing?
Thanks, Symon.
p.s. This thought came from a post comp.arch.fpga

--=========================Exported
Functions=================================

-- Id: A.1
function "abs" ( X : SIGNED) return SIGNED is
constant ARG_LEFT:INTEGER:= X'length-1;
alias XX : SIGNED(ARG_LEFT downto 0) is X;
variable RESULT: SIGNED (ARG_LEFT downto 0);
begin
if X'length<1 then return NAS; end if;
RESULT:=TO_01(xx,'X');
if (RESULT(RESULT'left)='X') then return RESULT; end if;
if RESULT(RESULT'left) = '1' then
RESULT:= -RESULT;
end if;
return RESULT;
end; -- "abs"


-- Id: A.2
function "-" ( ARG: SIGNED) return SIGNED is
constant ARG_LEFT:INTEGER:= ARG'length-1;
alias XARG: SIGNED(ARG_LEFT downto 0) is ARG;
variable RESULT,XARG01 : SIGNED(ARG_LEFT downto 0);
variable CBIT : STD_LOGIC:= '1';
begin
if ARG'length<1 then return NAS; end if;
XARG01 := TO_01(ARG,'X');
if (XARG01(XARG01'left)='X') then return XARG01; end if;
for i in 0 to RESULT'left loop
RESULT(i) := not(XARG01(i)) xor CBIT;
CBIT := CBIT and not(XARG01(i));
end loop;
return RESULT;
end; -- "-"


Pieter Hulshoff

unread,
Apr 22, 2010, 8:38:43 AM4/22/10
to
Symon wrote:
> So, if I read numeric_std correctly (see below), in VHDL, for (say) four
> bit signed numbers, abs("1000"), aka abs(-8), equals zero. What am I
> missing?

Considering that in two's complement, a 4 bit signed is defined from -8 to 7,
what result did you hope for? The lowest negative number in two's complement
is always a special case. It is up to you if you wish to check for it or not.

Kind regards,

Pieter Hulshoff

Symon

unread,
Apr 22, 2010, 10:49:31 AM4/22/10
to

Sure, I just wanted to be sure that's what happens. Before I looked in
numeric_std, I expected abs to take a signed argument and return an
unsigned value. However, it doesn't.
Thanks, Syms.

Rob Gaddi

unread,
Apr 22, 2010, 12:04:42 PM4/22/10
to

Yeah, numeric_std makes a couple of interesting choices regarding output
formats. I can understand the idea of always wanting your outputs to
remain the same type (signing and range) as your inputs, but it
definitely means that some math expressions require a truly ugly amount
of casting and '0' &.

--
Rob Gaddi, Highland Technology
Email address is currently out of order

Andy

unread,
Apr 22, 2010, 6:21:19 PM4/22/10
to
> Email address is currently out of order- Hide quoted text -
>
> - Show quoted text -

You could use fixed point with no fractional bits. It extends the size
to handle the largest possible result. But unfortunately, ufixed -
ufixed is still ufixed, so it does not always handle that correctly.

Andy

Andy Rushton

unread,
Apr 23, 2010, 5:56:05 AM4/23/10
to
Symon wrote:
>
> So, if I read numeric_std correctly (see below), in VHDL, for (say) four
> bit signed numbers, abs("1000"), aka abs(-8), equals zero. What am I
> missing?

No, for a 4-bit signed, abs(-8) = -8!

In 2's-complement notation, and therefore in numeric_std, the negation
of the most negative value is itself. So, in this case, the negation of
1000 (-8) is 1000 (-8).

If you consider what would happen if you sign-extended first, you can
see what's happening - 11000 (-8) gets negated to 01000 (+8).

This is a side-effect of the asymmetry in 2's-complement notation and
not a bug. Deciding what to do with asymmetrical number ranges is an
essential design decision in any design using either 2's-complement
integer or fixed-point numbers.

You can either decide to support the restricted range -7 to 7 and
consider -8 to be out of range, or you can extend the value to 5 bits to
include the whole representable range. If you want to support the full
range of the input type, resize one-bit bigger first (before the abs).

e.g. abs(resize(x,5))

Symon

unread,
Apr 23, 2010, 6:34:46 AM4/23/10
to
On 4/23/2010 10:56 AM, Andy Rushton wrote:
> Symon wrote:
>>
>> So, if I read numeric_std correctly (see below), in VHDL, for (say)
>> four bit signed numbers, abs("1000"), aka abs(-8), equals zero. What
>> am I missing?
>
> No, for a 4-bit signed, abs(-8) = -8!
>
Aha! OK, I see it now, many thanks.

Rob Gaddi

unread,
Apr 23, 2010, 12:19:40 PM4/23/10
to
On 4/23/2010 2:56 AM, Andy Rushton wrote:
> Symon wrote:
>>
>> So, if I read numeric_std correctly (see below), in VHDL, for (say)
>> four bit signed numbers, abs("1000"), aka abs(-8), equals zero. What
>> am I missing?
>
> No, for a 4-bit signed, abs(-8) = -8!
>
> In 2's-complement notation, and therefore in numeric_std, the negation
> of the most negative value is itself. So, in this case, the negation of
> 1000 (-8) is 1000 (-8).
>
> If you consider what would happen if you sign-extended first, you can
> see what's happening - 11000 (-8) gets negated to 01000 (+8).
>
> This is a side-effect of the asymmetry in 2's-complement notation and
> not a bug. Deciding what to do with asymmetrical number ranges is an
> essential design decision in any design using either 2's-complement
> integer or fixed-point numbers.
>
> You can either decide to support the restricted range -7 to 7 and
> consider -8 to be out of range, or you can extend the value to 5 bits to
> include the whole representable range. If you want to support the full
> range of the input type, resize one-bit bigger first (before the abs).
>
> e.g. abs(resize(x,5))
>
> [snip]

Or just copy/paste/tweak the numeric_std code to write your own abs
function that returns an unsigned of the same width as the signed input.

rickman

unread,
Apr 24, 2010, 12:22:29 PM4/24/10
to

I'm not sure I see the problem. If you need the result as a signed
number, you have to extend the result by one bit. This can be done
after the abs like this...

signed(resize(unsigned(abs(foo)), foo'length+1))

I guess that is a bit messy, but you can encapsulate this in a
function.

If you want the result as an unsigned number, you just re-type it...

unsigned(abs(foo)), foo'length)

The unsigned result fits in the same number of bits as the signed
value.

Rick

David Bishop

unread,
Apr 24, 2010, 12:47:33 PM4/24/10
to
Symon wrote:
> On 4/22/2010 3:26 AM, dlopez wrote:
>> Hi,
>> I'm trying to calculate the absolute value of a signed number (two's
>> complement).
>>
>> Right now, I sign extend the input, and when msb=1, inverse all bits and
>> add 1. The sign extend is to take care of the most negative number.
>>
>
> So, if I read numeric_std correctly (see below), in VHDL, for (say) four
> bit signed numbers, abs("1000"), aka abs(-8), equals zero. What am I
> missing?
> Thanks, Symon.
> p.s. This thought came from a post comp.arch.fpga

I noticed this when I did the fixed point package. If you use fixed
point then a bit gets added when you do an "abs".
Thus:
constant sf8 : sfixed (3 downto 0) := "1000";
variable sf8p : sfixed (4 downto 0);

...
sf8p := abs (sf8);

You will get "01000" as the result.

Andy Rushton

unread,
Apr 26, 2010, 7:00:02 AM4/26/10
to
Rob Gaddi wrote:
> >
> Or just copy/paste/tweak the numeric_std code to write your own abs
> function that returns an unsigned of the same width as the signed input.
>

Not a good idea. Synthesis tools do not necessarily synthesise the body
of the package, in fact they almost certainly do not. Typically
operators have straight mappings to hardware. These mappings may be
pre-optimised. If you copy the body code, you may lose those
optimisations brought about by the straight mappings.

If you want an unsigned result from signed abs:

signal x : signed(3 downto 0);
signal z : unsigned(3 downto 0);

z <= unsigned(abs x);

Notice that the result is the same size as the input. However, z has the
range 0 to 15 and x has the range -8 to 7. The value -8 will get mapped
by abs onto -8, but the type conversion to unsigned will map it onto +8.
So everything pans out OK in the end.

0 new messages