The most efficient way is to store the primitive numbers (Int Double...) as the raw type.

The unsignedness should be stored in a Type parameter, which will be erased at runtime. Scala does this when you let simple case classes extend AnyVal.

The folowing code does this for Ints, Longs, Doubles and Bigint.
I added a few classifications in adition to unsigned and renamed unsigned to positive.

Also since the classification is all done in the type-system we do not need to provide as many overloaded + - and * functions. This will save space when trying to implement this for all the number types.

There is still a little to be done when bridging between the various types. I will have a look at this later.

The Classification traits:

```
sealed trait SignTag{
type SubTag <:SignTag;
type AddTag <:SignTag;
type MultTag<:SignTag;
}
sealed trait Signed extends SignTag{
type SubTag=Signed;
type AddTag=Signed;
type MultTag=Signed;
}
sealed trait Positive extends SignTag{
type SubTag=Signed;
type AddTag=Negative;
type MultTag=Negative;
}
sealed trait Negative extends SignTag{
type SubTag=Signed;
type AddTag=Negative;
type MultTag=Positive;
}
sealed trait Zero extends SignTag{
type SubTag=Zero;
type AddTag=Zero;
type MultTag=Zero;
}
```

Int wrapper:

```
object SInt {
@inline
implicit def toSigned[T <: SignTag](int:SInt[T]):SInt[Signed]=int.asInstanceOf[SInt[Signed]];
@inline implicit def toLong[T <: SignTag](int:SInt[T]):SLong[T]=SLong(int.underlying);
@inline implicit def toDouble[T <: SignTag](int:SInt[T]):SDouble[T]=SDouble(int.underlying);
@inline implicit def toBig[T <: SignTag](int:SInt[T]):SBigInt[T]=SBigInt(int.underlying);
}
case class SInt[T <: SignTag](val underlying:Int) extends AnyVal{
def -(second: SInt[_ <: T#InTag]):SInt[T#SubTag]=new SInt[T#SubTag](underlying - second.underlying);
def +(second: SInt[_ <: T#InTag]):SInt[T#AddTag]=new SInt[T#AddTag](underlying + second.underlying);
def *(second: SInt[_ <: T#InTag]):SInt[T#MultTag]=new SInt[T#MultTag](underlying * second.underlying);
def assertSameType(other:SInt[T])={};
}
```

Long wrapper:

```
object SLong {
@inline
implicit def toSigned[T <: SignTag](int:SLong[T]):SLong[Signed]=int.asInstanceOf[SLong[Signed]];
@inline implicit def toDouble[T <: SignTag](int:SLong[T]):SDouble[T]=SDouble(int.underlying);
@inline implicit def toBig[T <: SignTag](int:SLong[T]):SBigInt[T]=SBigInt(int.underlying);
}
case class SLong[T <: SignTag](val underlying:Long) extends AnyVal{
def -(second: SLong[_ <: T#InTag]):SLong[T#SubTag]=new SLong[T#SubTag](underlying - second.underlying);
def +(second: SLong[_ <: T#InTag]):SLong[T#AddTag]=new SLong[T#AddTag](underlying + second.underlying);
def *(second: SLong[_ <: T#InTag]):SLong[T#MultTag]=new SLong[T#MultTag](underlying * second.underlying);
def assertSameType(other:SLong[T])={};
}
```

Double wrapper:

```
object SDouble {
@inline
implicit def toSigned[T <: SignTag](int:SDouble[T]):SDouble[Signed]=int.asInstanceOf[SDouble[Signed]];
}
case class SDouble[T <: SignTag](val underlying:Double) extends AnyVal{
def -(second: SDouble[_ <: T#InTag]):SDouble[T#SubTag]=new SDouble[T#SubTag](underlying - second.underlying);
def +(second: SDouble[_ <: T#InTag]):SDouble[T#AddTag]=new SDouble[T#AddTag](underlying + second.underlying);
def *(second: SDouble[_ <: T#InTag]):SDouble[T#MultTag]=new SDouble[T#MultTag](underlying * second.underlying);
def assertSameType(other:SDouble[T])={};
}
```

BigInt wrapper:

```
object SBigInt {
@inline
implicit def toSigned[T <: SignTag](int:SLong[T]):SLong[Signed]=int.asInstanceOf[SLong[Signed]];
@inline
implicit def toDouble[T <: SignTag](int:SBigInt[T]):SDouble[T]=SDouble(int.underlying.toDouble);
}
case class SBigInt[T <: SignTag](val underlying:BigInt) extends AnyVal{
def -(second: SBigInt[_ <: T#InTag]):SBigInt[T#SubTag]=new SBigInt[T#SubTag](underlying - second.underlying);
def +(second: SBigInt[_ <: T#InTag]):SBigInt[T#AddTag]=new SBigInt[T#AddTag](underlying + second.underlying);
def *(second: SBigInt[_ <: T#InTag]):SBigInt[T#MultTag]=new SBigInt[T#MultTag](underlying * second.underlying);
def assertSameType(other:SBigInt[T])={};
}
```

Test the syntax:

```
class CompileToTest {
val signed=new SInt[Signed](5);
val positive=new SInt[Positive](5);
val negative=new SInt[Negative](-5);
val zero=new SInt[Zero](0);
(signed + signed).assertSameType(signed);
(negative + signed).assertSameType(signed);
(positive - positive).assertSameType(signed);
(positive * negative).assertSameType(signed);
(zero + zero).assertSameType(zero);
val positiveDouble=SDouble[Positive](4.4)
val negativeDouble=SDouble[Negative](-4.4)
val signedDouble=SDouble[Signed](-4.4)
(positiveDouble * negativeDouble).assertSameType(signedDouble);
}
```

Ps. Haven't actually looked at the bytecode, but the docs sugests that this should be inlined and compiled down to primitives.

I just love this lanuage.