You don't need the view.
This should do what you want (change the 2 literal to a variable, I tested it w/ a 2).
The first query grabs the discount if there's a discount. The second (connected by union) would grab a penalty if there's a penalty, but of an amount above the first row's from_amount, and the third (connected by union) would grab the penalty if there is one and it's below the first row's from_amount.
You can test it here: http://sqlfiddle.com/#!4/d41d8/25188/0
with discounts as
( select 25 as from_amount, 39 as to_amount, .02 as discount from dual union all
select 40 as from_amount, 49 as to_amount, .05 as discount from dual union all
select 50 as from_amount, 99999 as to_amount, .10 as discount from dual )
, penalties as
( select 5 as from_amount, 9 as to_amount, .10 as penalty from dual union all
select 10 as from_amount, 19 as to_amount, .05 as penalty from dual)
select discount as change
from discounts
where 2 between from_amount and to_amount
union all
select -penalty as change
from penalties
where 2 between from_amount and to_amount
union all
select -penalty as change
from penalties
where 2 < (select min(from_amount) from penalties)
and from_amount = (select min(from_amount) from penalties)
Regarding your last edit, the query below would show "0" for any amount for which there is neither a penalty nor a discount (the version of my query above would just show no rows for such a situation). You may prefer that it show zero, like this:
select discount as change
from discounts
where 22 between from_amount and to_amount
union all
select -penalty as change
from penalties
where 22 between from_amount and to_amount
union all
select -penalty as change
from penalties
where 22 < (select min(from_amount) from penalties)
and from_amount = (select min(from_amount) from penalties)
union all
select 0 as change
from dual
where not exists (select 1 from discounts where 22 between from_amount and to_amount)
and not exists (select 1 from penalties where 22 between from_amount and to_amount)
and 22 >= (select min(from_amount) from penalties)
If you change the SQL for that view to the below, you should get the range in between to show zero:
select discounts.from_amount as from_amount,
discounts.to_amount as to_amount,
discounts.discount * -1 as change
from discounts
union
select penalties.from_amount as from_amount,
penalties.to_amount as to_amount,
penalties.penalty as change
from penalties
union
select p.to_amount + 1, d.from_amount - 1, 0 as change
from discounts d, penalties p
where d.from_amount = (select min(from_amount) from discounts) and
p.to_amount = (select max(to_amount) from penalties)
order by from_amount desc